@effect-app/infra 3.10.0 → 4.0.0-beta.0
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 +11 -0
- package/_check.sh +3 -0
- package/dist/CUPS.d.ts +22 -12
- package/dist/CUPS.d.ts.map +1 -1
- package/dist/CUPS.js +28 -29
- package/dist/Emailer/Sendgrid.js +13 -12
- package/dist/Emailer/service.d.ts +3 -13
- package/dist/Emailer/service.d.ts.map +1 -1
- package/dist/Emailer/service.js +3 -3
- package/dist/MainFiberSet.d.ts +18 -41
- package/dist/MainFiberSet.d.ts.map +1 -1
- package/dist/MainFiberSet.js +10 -10
- package/dist/Model/Repository/ext.d.ts.map +1 -1
- package/dist/Model/Repository/ext.js +13 -10
- package/dist/Model/Repository/internal/internal.d.ts +5 -5
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +52 -42
- package/dist/Model/Repository/legacy.d.ts +9 -9
- package/dist/Model/Repository/legacy.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.d.ts +4 -4
- package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.js +1 -1
- package/dist/Model/Repository/service.d.ts +11 -11
- package/dist/Model/Repository/service.d.ts.map +1 -1
- package/dist/Model/Repository/validation.d.ts +17 -47
- package/dist/Model/Repository/validation.d.ts.map +1 -1
- package/dist/Model/Repository/validation.js +2 -2
- package/dist/Model/query/dsl.d.ts +22 -22
- package/dist/Model/query/dsl.d.ts.map +1 -1
- package/dist/Model/query/dsl.js +1 -1
- package/dist/Model/query/new-kid-interpreter.d.ts +1 -1
- package/dist/Model/query/new-kid-interpreter.js +7 -7
- package/dist/Operations.d.ts +22 -63
- package/dist/Operations.d.ts.map +1 -1
- package/dist/Operations.js +14 -14
- package/dist/OperationsRepo.d.ts +23 -7
- package/dist/OperationsRepo.d.ts.map +1 -1
- package/dist/OperationsRepo.js +4 -5
- package/dist/QueueMaker/SQLQueue.d.ts +6 -8
- package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
- package/dist/QueueMaker/SQLQueue.js +20 -24
- package/dist/QueueMaker/errors.js +1 -1
- package/dist/QueueMaker/memQueue.d.ts +2 -5
- package/dist/QueueMaker/memQueue.d.ts.map +1 -1
- package/dist/QueueMaker/memQueue.js +22 -26
- package/dist/QueueMaker/sbqueue.d.ts +2 -5
- package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.js +24 -28
- package/dist/RequestContext.d.ts +28 -41
- package/dist/RequestContext.d.ts.map +1 -1
- package/dist/RequestContext.js +4 -4
- package/dist/RequestFiberSet.d.ts +23 -50
- package/dist/RequestFiberSet.d.ts.map +1 -1
- package/dist/RequestFiberSet.js +14 -14
- package/dist/Store/ContextMapContainer.d.ts +4 -4
- package/dist/Store/ContextMapContainer.d.ts.map +1 -1
- package/dist/Store/ContextMapContainer.js +5 -5
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +21 -28
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +12 -16
- package/dist/Store/Memory.d.ts +2 -2
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +25 -33
- package/dist/Store/index.js +2 -2
- package/dist/Store/service.d.ts +9 -34
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +4 -4
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +10 -2
- package/dist/adapters/SQL/Model.d.ts +106 -162
- package/dist/adapters/SQL/Model.d.ts.map +1 -1
- package/dist/adapters/SQL/Model.js +92 -130
- package/dist/adapters/ServiceBus.d.ts +13 -44
- package/dist/adapters/ServiceBus.d.ts.map +1 -1
- package/dist/adapters/ServiceBus.js +13 -15
- package/dist/adapters/cosmos-client.d.ts +7 -3
- package/dist/adapters/cosmos-client.d.ts.map +1 -1
- package/dist/adapters/cosmos-client.js +5 -4
- package/dist/adapters/logger.d.ts +1 -1
- package/dist/adapters/logger.d.ts.map +1 -1
- package/dist/adapters/memQueue.d.ts +8 -21
- package/dist/adapters/memQueue.d.ts.map +1 -1
- package/dist/adapters/memQueue.js +4 -4
- package/dist/adapters/mongo-client.d.ts +6 -6
- package/dist/adapters/mongo-client.d.ts.map +1 -1
- package/dist/adapters/mongo-client.js +5 -4
- package/dist/adapters/redis-client.d.ts +14 -4
- package/dist/adapters/redis-client.d.ts.map +1 -1
- package/dist/adapters/redis-client.js +19 -18
- package/dist/api/ContextProvider.d.ts +10 -15
- package/dist/api/ContextProvider.d.ts.map +1 -1
- package/dist/api/ContextProvider.js +8 -8
- package/dist/api/codec.d.ts +1 -1
- package/dist/api/codec.d.ts.map +1 -1
- package/dist/api/codec.js +1 -1
- package/dist/api/internal/RequestContextMiddleware.d.ts +1 -1
- package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
- package/dist/api/internal/auth.d.ts +3 -3
- package/dist/api/internal/auth.d.ts.map +1 -1
- package/dist/api/internal/auth.js +8 -8
- 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 +9 -9
- package/dist/api/internal/health.d.ts +1 -1
- package/dist/api/internal/health.d.ts.map +1 -1
- package/dist/api/internal/health.js +2 -2
- package/dist/api/layerUtils.d.ts +14 -14
- package/dist/api/layerUtils.d.ts.map +1 -1
- package/dist/api/layerUtils.js +5 -5
- package/dist/api/middlewares.d.ts +0 -75
- package/dist/api/middlewares.d.ts.map +1 -1
- package/dist/api/middlewares.js +6 -51
- package/dist/api/reportError.js +4 -4
- package/dist/api/routing/middleware/RouterMiddleware.d.ts +4 -4
- package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.d.ts +6 -7
- package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
- package/dist/api/routing/middleware/middleware.js +9 -13
- 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 +5 -4
- package/dist/api/routing/utils.d.ts +2 -2
- package/dist/api/routing/utils.d.ts.map +1 -1
- package/dist/api/routing/utils.js +10 -8
- package/dist/api/routing.d.ts +39 -37
- package/dist/api/routing.d.ts.map +1 -1
- package/dist/api/routing.js +17 -21
- package/dist/api/setupRequest.d.ts +4 -6
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +10 -9
- package/dist/arbs.d.ts +3 -3
- package/dist/arbs.d.ts.map +1 -1
- package/dist/arbs.js +2 -2
- package/dist/errorReporter.d.ts +1 -1
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +12 -12
- package/dist/fileUtil.d.ts +6 -6
- package/dist/fileUtil.d.ts.map +1 -1
- package/dist/logger/jsonLogger.d.ts.map +1 -1
- package/dist/logger/jsonLogger.js +19 -18
- package/dist/logger/logFmtLogger.d.ts.map +1 -1
- package/dist/logger/logFmtLogger.js +11 -13
- package/dist/logger/shared.d.ts +2 -2
- package/dist/logger/shared.d.ts.map +1 -1
- package/dist/logger/shared.js +7 -9
- package/dist/logger.d.ts +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/rateLimit.d.ts +2 -2
- package/dist/rateLimit.d.ts.map +1 -1
- package/dist/rateLimit.js +5 -5
- package/dist/test.d.ts +2 -2
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +6 -24
- package/package.json +19 -22
- package/src/CUPS.ts +15 -14
- package/src/Emailer/Sendgrid.ts +15 -13
- package/src/Emailer/service.ts +3 -3
- package/src/MainFiberSet.ts +16 -12
- package/src/Model/Repository/ext.ts +18 -16
- package/src/Model/Repository/internal/internal.ts +80 -69
- package/src/Model/Repository/legacy.ts +9 -9
- package/src/Model/Repository/makeRepo.ts +5 -5
- package/src/Model/Repository/service.ts +12 -12
- package/src/Model/Repository/validation.ts +1 -1
- package/src/Model/query/dsl.ts +13 -13
- package/src/Model/query/new-kid-interpreter.ts +8 -8
- package/src/Operations.ts +17 -14
- package/src/OperationsRepo.ts +3 -4
- package/src/QueueMaker/SQLQueue.ts +86 -89
- package/src/QueueMaker/errors.ts +1 -1
- package/src/QueueMaker/memQueue.ts +90 -91
- package/src/QueueMaker/sbqueue.ts +90 -92
- package/src/RequestContext.ts +3 -3
- package/src/RequestFiberSet.ts +17 -15
- package/src/Store/ContextMapContainer.ts +4 -4
- package/src/Store/Cosmos.ts +20 -27
- package/src/Store/Disk.ts +13 -17
- package/src/Store/Memory.ts +28 -34
- package/src/Store/index.ts +1 -1
- package/src/Store/service.ts +4 -4
- package/src/Store/utils.ts +9 -5
- package/src/adapters/SQL/Model.ts +255 -268
- package/src/adapters/ServiceBus.ts +17 -20
- package/src/adapters/cosmos-client.ts +5 -5
- package/src/adapters/memQueue.ts +3 -3
- package/src/adapters/mongo-client.ts +5 -5
- package/src/adapters/redis-client.ts +25 -19
- package/src/api/ContextProvider.ts +24 -34
- package/src/api/codec.ts +1 -1
- package/src/api/internal/auth.ts +11 -13
- package/src/api/internal/events.ts +11 -11
- package/src/api/internal/health.ts +1 -1
- package/src/api/layerUtils.ts +20 -20
- package/src/api/middlewares.ts +0 -97
- package/src/api/reportError.ts +3 -3
- package/src/api/routing/middleware/RouterMiddleware.ts +5 -6
- package/src/api/routing/middleware/middleware.ts +13 -25
- package/src/api/routing/schema/jwt.ts +9 -7
- package/src/api/routing/utils.ts +12 -10
- package/src/api/routing.ts +77 -79
- package/src/api/setupRequest.ts +9 -8
- package/src/arbs.ts +3 -3
- package/src/errorReporter.ts +12 -12
- package/src/logger/jsonLogger.ts +18 -17
- package/src/logger/logFmtLogger.ts +10 -12
- package/src/logger/shared.ts +6 -8
- package/src/rateLimit.ts +7 -7
- package/src/test.ts +7 -29
- package/test/contextProvider.test.ts +77 -70
- package/test/controller.test.ts +51 -39
- 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 +33 -81
- package/test/dist/fixtures.d.ts.map +1 -1
- package/test/dist/fixtures.js +9 -8
- 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/fixtures.ts +9 -7
- package/test/query.test.ts +49 -41
- package/test/rawQuery.test.ts +44 -40
- package/test/requires.test.ts +40 -31
- package/test/rpc-multi-middleware.test.ts +13 -14
- package/test/validateSample.test.ts +2 -2
- package/tsconfig.json +1 -27
- package/dist/api/internal/middlewares.d.ts +0 -15
- package/dist/api/internal/middlewares.d.ts.map +0 -1
- package/dist/api/internal/middlewares.js +0 -168
- package/src/api/internal/middlewares.ts +0 -279
package/src/Store/Cosmos.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
|
|
3
|
-
import { Array,
|
|
3
|
+
import { Array, Duration, Effect, Layer, type NonEmptyReadonlyArray, Option, pipe, Redacted, Struct } from "effect-app"
|
|
4
4
|
import { toNonEmptyArray } from "effect-app/Array"
|
|
5
5
|
import { dropUndefinedT, mutable } from "effect-app/utils"
|
|
6
6
|
import { CosmosClient, CosmosClientLayer } from "../adapters/cosmos-client.js"
|
|
@@ -70,12 +70,12 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
70
70
|
(x) =>
|
|
71
71
|
[
|
|
72
72
|
x,
|
|
73
|
-
Option.match(Option.
|
|
73
|
+
Option.match(Option.fromNullishOr(x._etag), {
|
|
74
74
|
onNone: () =>
|
|
75
75
|
dropUndefinedT({
|
|
76
76
|
operationType: "Create" as const,
|
|
77
77
|
resourceBody: {
|
|
78
|
-
...Struct.omit(x, "_etag", idKey),
|
|
78
|
+
...Struct.omit(x, ["_etag", idKey]),
|
|
79
79
|
id: x[idKey],
|
|
80
80
|
_partitionKey: config?.partitionValue(x)
|
|
81
81
|
}
|
|
@@ -87,7 +87,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
87
87
|
operationType: "Replace" as const,
|
|
88
88
|
id: x[idKey],
|
|
89
89
|
resourceBody: {
|
|
90
|
-
...Struct.omit(x, "_etag", idKey),
|
|
90
|
+
...Struct.omit(x, ["_etag", idKey]),
|
|
91
91
|
id: x[idKey],
|
|
92
92
|
_partitionKey: config?.partitionValue(x)
|
|
93
93
|
},
|
|
@@ -98,7 +98,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
98
98
|
})
|
|
99
99
|
] as const
|
|
100
100
|
)
|
|
101
|
-
const batches =
|
|
101
|
+
const batches = Array.chunksOf(b, config?.maxBulkSize ?? 10)
|
|
102
102
|
|
|
103
103
|
const batchResult = yield* Effect.forEach(
|
|
104
104
|
batches
|
|
@@ -162,9 +162,8 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
162
162
|
return batchResult.flat() as unknown as NonEmptyReadonlyArray<Encoded>
|
|
163
163
|
})
|
|
164
164
|
.pipe(Effect.withSpan("Cosmos.bulkSet [effect-app/infra/Store]", {
|
|
165
|
-
captureStackTrace: false,
|
|
166
165
|
attributes: { "repository.container_id": containerId, "repository.model_name": name }
|
|
167
|
-
}))
|
|
166
|
+
}, { captureStackTrace: false }))
|
|
168
167
|
|
|
169
168
|
const batchSet = (items: NonEmptyReadonlyArray<PM>) => {
|
|
170
169
|
return Effect
|
|
@@ -173,11 +172,11 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
173
172
|
(x) =>
|
|
174
173
|
[
|
|
175
174
|
x,
|
|
176
|
-
Option.match(Option.
|
|
175
|
+
Option.match(Option.fromNullishOr(x._etag), {
|
|
177
176
|
onNone: () => ({
|
|
178
177
|
operationType: "Create" as const,
|
|
179
178
|
resourceBody: {
|
|
180
|
-
...Struct.omit(x, "_etag", idKey),
|
|
179
|
+
...Struct.omit(x, ["_etag", idKey]),
|
|
181
180
|
id: x[idKey],
|
|
182
181
|
_partitionKey: config?.partitionValue(x)
|
|
183
182
|
}
|
|
@@ -188,7 +187,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
188
187
|
operationType: "Replace" as const,
|
|
189
188
|
id: x[idKey],
|
|
190
189
|
resourceBody: {
|
|
191
|
-
...Struct.omit(x, "_etag", idKey),
|
|
190
|
+
...Struct.omit(x, ["_etag", idKey]),
|
|
192
191
|
id: x[idKey],
|
|
193
192
|
_partitionKey: config?.partitionValue(x)
|
|
194
193
|
},
|
|
@@ -228,9 +227,8 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
228
227
|
})
|
|
229
228
|
.pipe(Effect
|
|
230
229
|
.withSpan("Cosmos.batchSet [effect-app/infra/Store]", {
|
|
231
|
-
captureStackTrace: false,
|
|
232
230
|
attributes: { "repository.container_id": containerId, "repository.model_name": name }
|
|
233
|
-
}))
|
|
231
|
+
}, { captureStackTrace: false }))
|
|
234
232
|
}
|
|
235
233
|
|
|
236
234
|
const s: Store<IdKey, Encoded> = {
|
|
@@ -254,9 +252,8 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
254
252
|
),
|
|
255
253
|
Effect
|
|
256
254
|
.withSpan("Cosmos.queryRaw [effect-app/infra/Store]", {
|
|
257
|
-
captureStackTrace: false,
|
|
258
255
|
attributes: { "repository.container_id": containerId, "repository.model_name": name }
|
|
259
|
-
})
|
|
256
|
+
}, { captureStackTrace: false })
|
|
260
257
|
),
|
|
261
258
|
batchRemove: (ids, partitionKey?: string) =>
|
|
262
259
|
Effect.promise(() =>
|
|
@@ -294,9 +291,8 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
294
291
|
),
|
|
295
292
|
Effect
|
|
296
293
|
.withSpan("Cosmos.all [effect-app/infra/Store]", {
|
|
297
|
-
captureStackTrace: false,
|
|
298
294
|
attributes: { "repository.container_id": containerId, "repository.model_name": name }
|
|
299
|
-
})
|
|
295
|
+
}, { captureStackTrace: false })
|
|
300
296
|
),
|
|
301
297
|
/**
|
|
302
298
|
* May return duplicate results for "join_find", when matching more than once.
|
|
@@ -336,7 +332,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
336
332
|
({
|
|
337
333
|
...pipe(
|
|
338
334
|
defaultValues,
|
|
339
|
-
Struct.pick(
|
|
335
|
+
Struct.pick(f.select!.filter((_) => typeof _ === "string"))
|
|
340
336
|
),
|
|
341
337
|
...mapReverseId(_ as any)
|
|
342
338
|
}) as any
|
|
@@ -354,9 +350,8 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
354
350
|
)
|
|
355
351
|
.pipe(
|
|
356
352
|
Effect.withSpan("Cosmos.filter [effect-app/infra/Store]", {
|
|
357
|
-
captureStackTrace: false,
|
|
358
353
|
attributes: { "repository.container_id": containerId, "repository.model_name": name }
|
|
359
|
-
})
|
|
354
|
+
}, { captureStackTrace: false })
|
|
360
355
|
)
|
|
361
356
|
},
|
|
362
357
|
find: (id) =>
|
|
@@ -366,24 +361,23 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
366
361
|
.item(id, config?.partitionValue({ [idKey]: id } as Encoded))
|
|
367
362
|
.read<Encoded>()
|
|
368
363
|
.then(({ resource }) =>
|
|
369
|
-
Option.
|
|
364
|
+
Option.fromNullishOr(resource).pipe(Option.map((_) => ({ ...defaultValues, ...mapReverseId(_) })))
|
|
370
365
|
)
|
|
371
366
|
)
|
|
372
367
|
.pipe(Effect
|
|
373
368
|
.withSpan("Cosmos.find [effect-app/infra/Store]", {
|
|
374
|
-
captureStackTrace: false,
|
|
375
369
|
attributes: {
|
|
376
370
|
"repository.container_id": containerId,
|
|
377
371
|
"repository.model_name": name,
|
|
378
372
|
partitionValue: config?.partitionValue({ [idKey]: id } as Encoded),
|
|
379
373
|
id
|
|
380
374
|
}
|
|
381
|
-
})),
|
|
375
|
+
}, { captureStackTrace: false })),
|
|
382
376
|
set: (e) =>
|
|
383
377
|
Option
|
|
384
378
|
.match(
|
|
385
379
|
Option
|
|
386
|
-
.
|
|
380
|
+
.fromNullishOr(e._etag),
|
|
387
381
|
{
|
|
388
382
|
onNone: () =>
|
|
389
383
|
Effect.promise(() =>
|
|
@@ -410,7 +404,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
410
404
|
Effect
|
|
411
405
|
.flatMap((x) => {
|
|
412
406
|
if (x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409) {
|
|
413
|
-
return new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode })
|
|
407
|
+
return Effect.fail(new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode }))
|
|
414
408
|
}
|
|
415
409
|
if (x.statusCode > 299 || x.statusCode < 200) {
|
|
416
410
|
return Effect.die(
|
|
@@ -426,13 +420,12 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
426
420
|
}),
|
|
427
421
|
Effect
|
|
428
422
|
.withSpan("Cosmos.set [effect-app/infra/Store]", {
|
|
429
|
-
captureStackTrace: false,
|
|
430
423
|
attributes: {
|
|
431
424
|
"repository.container_id": containerId,
|
|
432
425
|
"repository.model_name": name,
|
|
433
426
|
id: e[idKey]
|
|
434
427
|
}
|
|
435
|
-
})
|
|
428
|
+
}, { captureStackTrace: false })
|
|
436
429
|
),
|
|
437
430
|
batchSet,
|
|
438
431
|
bulkSet
|
|
@@ -443,7 +436,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
443
436
|
container
|
|
444
437
|
.item(importedMarkerId, importedMarkerId)
|
|
445
438
|
.read<{ id: string }>()
|
|
446
|
-
.then(({ resource }) => Option.
|
|
439
|
+
.then(({ resource }) => Option.fromNullishOr(resource))
|
|
447
440
|
)
|
|
448
441
|
|
|
449
442
|
if (!Option.isSome(marker)) {
|
package/src/Store/Disk.ts
CHANGED
|
@@ -3,7 +3,7 @@ import * as fu from "../fileUtil.js"
|
|
|
3
3
|
|
|
4
4
|
import fs from "fs"
|
|
5
5
|
|
|
6
|
-
import { Console, Effect, flow } from "effect-app"
|
|
6
|
+
import { Console, Effect, flow, Semaphore } from "effect-app"
|
|
7
7
|
import type { FieldValues } from "../Model/filter/types.js"
|
|
8
8
|
import { makeMemoryStoreInt, storeId } from "./Memory.js"
|
|
9
9
|
import { type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "./service.js"
|
|
@@ -30,26 +30,24 @@ function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValu
|
|
|
30
30
|
get: fu
|
|
31
31
|
.readTextFile(file)
|
|
32
32
|
.pipe(
|
|
33
|
-
Effect.withSpan("Disk.read.readFile [effect-app/infra/Store]", { captureStackTrace: false }),
|
|
33
|
+
Effect.withSpan("Disk.read.readFile [effect-app/infra/Store]", {}, { captureStackTrace: false }),
|
|
34
34
|
Effect.flatMap((x) =>
|
|
35
35
|
Effect.sync(() => JSON.parse(x) as PM[]).pipe(
|
|
36
|
-
Effect.withSpan("Disk.read.parse [effect-app/infra/Store]", { captureStackTrace: false })
|
|
36
|
+
Effect.withSpan("Disk.read.parse [effect-app/infra/Store]", {}, { captureStackTrace: false })
|
|
37
37
|
)
|
|
38
38
|
),
|
|
39
39
|
Effect.orDie,
|
|
40
40
|
Effect.withSpan("Disk.read [effect-app/infra/Store]", {
|
|
41
|
-
captureStackTrace: false,
|
|
42
41
|
attributes: { "disk.file": file }
|
|
43
|
-
})
|
|
42
|
+
}, { captureStackTrace: false })
|
|
44
43
|
),
|
|
45
44
|
setRaw: (v: Iterable<PM>) =>
|
|
46
45
|
Effect
|
|
47
46
|
.sync(() => JSON.stringify([...v], undefined, 2))
|
|
48
47
|
.pipe(
|
|
49
48
|
Effect.withSpan("Disk.stringify [effect-app/infra/Store]", {
|
|
50
|
-
captureStackTrace: false,
|
|
51
49
|
attributes: { "disk.file": file }
|
|
52
|
-
}),
|
|
50
|
+
}, { captureStackTrace: false }),
|
|
53
51
|
Effect
|
|
54
52
|
.flatMap(
|
|
55
53
|
(json) =>
|
|
@@ -57,15 +55,13 @@ function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValu
|
|
|
57
55
|
.writeTextFile(file, json)
|
|
58
56
|
.pipe(Effect
|
|
59
57
|
.withSpan("Disk.write.writeFile [effect-app/infra/Store]", {
|
|
60
|
-
captureStackTrace: false,
|
|
61
58
|
attributes: { "disk.file_size": json.length }
|
|
62
|
-
}))
|
|
59
|
+
}, { captureStackTrace: false }))
|
|
63
60
|
),
|
|
64
61
|
Effect
|
|
65
62
|
.withSpan("Disk.write [effect-app/infra/Store]", {
|
|
66
|
-
captureStackTrace: false,
|
|
67
63
|
attributes: { "disk.file": file }
|
|
68
|
-
})
|
|
64
|
+
}, { captureStackTrace: false })
|
|
69
65
|
)
|
|
70
66
|
}
|
|
71
67
|
|
|
@@ -95,14 +91,14 @@ function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValu
|
|
|
95
91
|
})
|
|
96
92
|
)
|
|
97
93
|
|
|
98
|
-
const sem =
|
|
94
|
+
const sem = Semaphore.makeUnsafe(1)
|
|
99
95
|
const withPermit = sem.withPermits(1)
|
|
100
96
|
const flushToDisk = Effect.flatMap(store.all, fsStore.setRaw).pipe(withPermit)
|
|
101
97
|
const flushToDiskInBackground = flushToDisk
|
|
102
98
|
.pipe(
|
|
103
|
-
Effect.
|
|
99
|
+
Effect.tapCause(Console.error),
|
|
104
100
|
Effect.uninterruptible,
|
|
105
|
-
Effect.
|
|
101
|
+
Effect.forkDetach
|
|
106
102
|
)
|
|
107
103
|
|
|
108
104
|
return {
|
|
@@ -144,13 +140,13 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
|
|
|
144
140
|
config?: StoreConfig<Encoded>
|
|
145
141
|
) =>
|
|
146
142
|
Effect.gen(function*() {
|
|
147
|
-
const storesSem =
|
|
143
|
+
const storesSem = Semaphore.makeUnsafe(1)
|
|
148
144
|
const primary = yield* makeDiskStoreInt(prefix, idKey, "primary", dir, name, seed, config?.defaultValues)
|
|
149
145
|
const stores = new Map<string, Store<IdKey, Encoded>>([["primary", primary]])
|
|
150
|
-
const ctx = yield* Effect.
|
|
146
|
+
const ctx = yield* Effect.services<R>()
|
|
151
147
|
const getStore = !config?.allowNamespace
|
|
152
148
|
? Effect.succeed(primary)
|
|
153
|
-
: storeId.pipe(Effect.flatMap((namespace) => {
|
|
149
|
+
: storeId.asEffect().pipe(Effect.flatMap((namespace) => {
|
|
154
150
|
const store = stores.get(namespace)
|
|
155
151
|
if (store) {
|
|
156
152
|
return Effect.succeed(store)
|
package/src/Store/Memory.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
|
|
3
|
-
import { Array,
|
|
3
|
+
import { Array, Effect, flow, type NonEmptyReadonlyArray, Option, Order, pipe, Ref, Result, Semaphore, ServiceMap, 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"
|
|
7
7
|
import type { FieldValues } from "../Model/filter/types.js"
|
|
8
|
-
import { codeFilter } from "./codeFilter.js"
|
|
8
|
+
import { codeFilter, codeFilter3_ } from "./codeFilter.js"
|
|
9
9
|
import { type FilterArgs, type PersistenceModelType, type Store, type StoreConfig, StoreMaker } from "./service.js"
|
|
10
10
|
import { makeUpdateETag } from "./utils.js"
|
|
11
11
|
|
|
@@ -18,20 +18,20 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
18
18
|
return r.map((i) => {
|
|
19
19
|
const [keys, subKeys] = pipe(
|
|
20
20
|
sel,
|
|
21
|
-
Array.
|
|
22
|
-
typeof r === "string" ?
|
|
21
|
+
Array.partition((r) =>
|
|
22
|
+
typeof r === "string" ? Result.fail(String(r)) : Result.succeed(r as { key: string; subKeys: string[] })
|
|
23
23
|
)
|
|
24
24
|
)
|
|
25
|
-
const n = Struct.pick(i,
|
|
25
|
+
const n = Struct.pick(i, keys)
|
|
26
26
|
subKeys.forEach((subKey) => {
|
|
27
|
-
n[subKey.key] = i[subKey.key]!.map(Struct.pick(
|
|
27
|
+
n[subKey.key] = i[subKey.key]!.map(Struct.pick(subKey.subKeys))
|
|
28
28
|
})
|
|
29
29
|
return n as M
|
|
30
30
|
}) as any
|
|
31
31
|
}
|
|
32
32
|
const skip = f?.skip
|
|
33
33
|
const limit = f?.limit
|
|
34
|
-
const ords = Option.map(Option.
|
|
34
|
+
const ords = Option.map(Option.fromNullishOr(f.order), (_) =>
|
|
35
35
|
_.map((_) =>
|
|
36
36
|
Order.make<T>((self, that) => {
|
|
37
37
|
// TODO: inspect data types for the right comparison?
|
|
@@ -59,7 +59,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
59
59
|
)
|
|
60
60
|
)
|
|
61
61
|
}
|
|
62
|
-
let r = f.filter ? Array.
|
|
62
|
+
let r = f.filter ? Array.filter(c, (x) => codeFilter3_(f.filter!, x)) : c
|
|
63
63
|
if (skip) {
|
|
64
64
|
r = Array.drop(r, skip)
|
|
65
65
|
}
|
|
@@ -71,9 +71,9 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
|
|
|
71
71
|
})
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
const defaultNs = NonEmptyString255("primary")
|
|
74
|
+
const defaultNs: NonEmptyString255 = NonEmptyString255("primary")
|
|
75
75
|
export class storeId
|
|
76
|
-
extends
|
|
76
|
+
extends ServiceMap.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs })
|
|
77
77
|
{}
|
|
78
78
|
|
|
79
79
|
function logQuery(f: FilterArgs<any, any>, defaultValues?: any) {
|
|
@@ -107,8 +107,8 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
107
107
|
const defaultValues = _defaultValues ?? {}
|
|
108
108
|
|
|
109
109
|
const items = new Map([...items_].map((_) => [_[idKey], { _etag: undefined, ...defaultValues, ..._ }] as const))
|
|
110
|
-
const store = Ref.
|
|
111
|
-
const sem =
|
|
110
|
+
const store = Ref.makeUnsafe<ReadonlyMap<Encoded[IdKey], PM>>(items)
|
|
111
|
+
const sem = Semaphore.makeUnsafe(1)
|
|
112
112
|
const withPermit = sem.withPermits(1)
|
|
113
113
|
const values = Effect.map(Ref.get(store), (s) => s.values())
|
|
114
114
|
|
|
@@ -159,31 +159,28 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
159
159
|
// Effect.tap(() => logQuery(query, defaultValues)),
|
|
160
160
|
Effect.map(query.memory),
|
|
161
161
|
Effect.withSpan("Memory.queryRaw [effect-app/infra/Store]", {
|
|
162
|
-
captureStackTrace: false,
|
|
163
162
|
attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
|
|
164
|
-
})
|
|
163
|
+
}, { captureStackTrace: false })
|
|
165
164
|
),
|
|
166
165
|
|
|
167
166
|
all: all.pipe(Effect.withSpan("Memory.all [effect-app/infra/Store]", {
|
|
168
|
-
captureStackTrace: false,
|
|
169
167
|
attributes: {
|
|
170
168
|
modelName,
|
|
171
169
|
namespace
|
|
172
170
|
}
|
|
173
|
-
})),
|
|
171
|
+
}, { captureStackTrace: false })),
|
|
174
172
|
find: (id) =>
|
|
175
173
|
Ref
|
|
176
174
|
.get(store)
|
|
177
175
|
.pipe(
|
|
178
|
-
Effect.map((_) => Option.
|
|
176
|
+
Effect.map((_) => Option.fromNullishOr(_.get(id))),
|
|
179
177
|
Effect
|
|
180
178
|
.withSpan("Memory.find [effect-app/infra/Store]", {
|
|
181
|
-
captureStackTrace: false,
|
|
182
179
|
attributes: {
|
|
183
180
|
modelName,
|
|
184
181
|
namespace
|
|
185
182
|
}
|
|
186
|
-
})
|
|
183
|
+
}, { captureStackTrace: false })
|
|
187
184
|
),
|
|
188
185
|
filter: (f) =>
|
|
189
186
|
all
|
|
@@ -191,9 +188,8 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
191
188
|
Effect.tap(() => logQuery(f, defaultValues)),
|
|
192
189
|
Effect.map(memFilter(f)),
|
|
193
190
|
Effect.withSpan("Memory.filter [effect-app/infra/Store]", {
|
|
194
|
-
captureStackTrace: false,
|
|
195
191
|
attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
|
|
196
|
-
})
|
|
192
|
+
}, { captureStackTrace: false })
|
|
197
193
|
),
|
|
198
194
|
set: (e) =>
|
|
199
195
|
s
|
|
@@ -210,9 +206,8 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
210
206
|
withPermit,
|
|
211
207
|
Effect
|
|
212
208
|
.withSpan("Memory.set [effect-app/infra/Store]", {
|
|
213
|
-
captureStackTrace: false,
|
|
214
209
|
attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
|
|
215
|
-
})
|
|
210
|
+
}, { captureStackTrace: false })
|
|
216
211
|
),
|
|
217
212
|
batchRemove: (items: NonEmptyReadonlyArray<Encoded[IdKey]>) =>
|
|
218
213
|
pipe(
|
|
@@ -220,13 +215,13 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
220
215
|
.sync(() => items)
|
|
221
216
|
// align with CosmosDB
|
|
222
217
|
.pipe(
|
|
223
|
-
Effect.
|
|
218
|
+
Effect.filterOrFail((_) => _.length <= 100, () => "BatchRemove: a batch may not exceed 100 items"),
|
|
219
|
+
Effect.orDie,
|
|
224
220
|
Effect.andThen(batchRemove),
|
|
225
221
|
Effect
|
|
226
222
|
.withSpan("Memory.batchRemove [effect-app/infra/Store]", {
|
|
227
|
-
captureStackTrace: false,
|
|
228
223
|
attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
|
|
229
|
-
})
|
|
224
|
+
}, { captureStackTrace: false })
|
|
230
225
|
)
|
|
231
226
|
),
|
|
232
227
|
batchSet: (items: readonly [PM, ...PM[]]) =>
|
|
@@ -235,22 +230,21 @@ export function makeMemoryStoreInt<IdKey extends keyof Encoded, Encoded extends
|
|
|
235
230
|
.sync(() => items)
|
|
236
231
|
// align with CosmosDB
|
|
237
232
|
.pipe(
|
|
238
|
-
Effect.
|
|
233
|
+
Effect.filterOrFail((_) => _.length <= 100, () => "BatchSet: a batch may not exceed 100 items"),
|
|
234
|
+
Effect.orDie,
|
|
239
235
|
Effect.andThen(batchSet),
|
|
240
236
|
Effect
|
|
241
237
|
.withSpan("Memory.batchSet [effect-app/infra/Store]", {
|
|
242
|
-
captureStackTrace: false,
|
|
243
238
|
attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
|
|
244
|
-
})
|
|
239
|
+
}, { captureStackTrace: false })
|
|
245
240
|
)
|
|
246
241
|
),
|
|
247
242
|
bulkSet: flow(
|
|
248
243
|
batchSet,
|
|
249
244
|
(_) =>
|
|
250
245
|
_.pipe(Effect.withSpan("Memory.bulkSet [effect-app/infra/Store]", {
|
|
251
|
-
captureStackTrace: false,
|
|
252
246
|
attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
|
|
253
|
-
}))
|
|
247
|
+
}, { captureStackTrace: false }))
|
|
254
248
|
)
|
|
255
249
|
}
|
|
256
250
|
return s
|
|
@@ -265,7 +259,7 @@ export const makeMemoryStore = () => ({
|
|
|
265
259
|
config?: StoreConfig<Encoded>
|
|
266
260
|
) =>
|
|
267
261
|
Effect.gen(function*() {
|
|
268
|
-
const storesSem =
|
|
262
|
+
const storesSem = Semaphore.makeUnsafe(1)
|
|
269
263
|
const primary = yield* makeMemoryStoreInt<IdKey, Encoded, R, E>(
|
|
270
264
|
modelName,
|
|
271
265
|
idKey,
|
|
@@ -273,11 +267,11 @@ export const makeMemoryStore = () => ({
|
|
|
273
267
|
seed,
|
|
274
268
|
config?.defaultValues
|
|
275
269
|
)
|
|
276
|
-
const ctx = yield* Effect.
|
|
270
|
+
const ctx = yield* Effect.services<R>()
|
|
277
271
|
const stores = new Map([["primary", primary]])
|
|
278
272
|
const getStore = !config?.allowNamespace
|
|
279
273
|
? Effect.succeed(primary)
|
|
280
|
-
: storeId.pipe(Effect.flatMap((namespace) => {
|
|
274
|
+
: storeId.asEffect().pipe(Effect.flatMap((namespace) => {
|
|
281
275
|
const store = stores.get(namespace)
|
|
282
276
|
if (store) {
|
|
283
277
|
return Effect.succeed(store)
|
package/src/Store/index.ts
CHANGED
package/src/Store/service.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import type { UniqueKey } from "@azure/cosmos"
|
|
3
|
-
import {
|
|
3
|
+
import { Effect, type NonEmptyReadonlyArray, type Option, type Redacted, ServiceMap } from "effect-app"
|
|
4
4
|
import type { OptimisticConcurrencyException } from "../errors.js"
|
|
5
5
|
import type { FilterResult } from "../Model/filter/filterApi.js"
|
|
6
6
|
import type { FieldValues } from "../Model/filter/types.js"
|
|
@@ -89,14 +89,14 @@ export interface Store<
|
|
|
89
89
|
queryRaw: <Out>(query: RawQuery<Encoded, Out>) => Effect.Effect<readonly Out[]>
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
export class StoreMaker extends
|
|
92
|
+
export class StoreMaker extends ServiceMap.Opaque<StoreMaker, {
|
|
93
93
|
make: <IdKey extends keyof Encoded, Encoded extends FieldValues, R = never, E = never>(
|
|
94
94
|
name: string,
|
|
95
95
|
idKey: IdKey,
|
|
96
96
|
seed?: Effect.Effect<Iterable<Encoded>, E, R>,
|
|
97
97
|
config?: StoreConfig<Encoded>
|
|
98
98
|
) => Effect.Effect<Store<IdKey, Encoded>, E, R>
|
|
99
|
-
}>() {
|
|
99
|
+
}>()("effect-app/StoreMaker") {
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
export const makeContextMap = () => {
|
|
@@ -170,7 +170,7 @@ export const makeContextMap = () => {
|
|
|
170
170
|
|
|
171
171
|
const makeMap = Effect.sync(() => makeContextMap())
|
|
172
172
|
|
|
173
|
-
export class ContextMap extends
|
|
173
|
+
export class ContextMap extends ServiceMap.Opaque<ContextMap>()("effect-app/ContextMap", { make: makeMap }) {
|
|
174
174
|
}
|
|
175
175
|
|
|
176
176
|
export type PersistenceModelType<Encoded extends object> = Encoded & {
|
package/src/Store/utils.ts
CHANGED
|
@@ -17,11 +17,15 @@ export const makeUpdateETag =
|
|
|
17
17
|
<IdKey extends keyof E, E extends PersistenceModelType<{}>>(e: E, idKey: IdKey, current: Option.Option<E>) =>
|
|
18
18
|
Effect.gen(function*() {
|
|
19
19
|
if (e._etag) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
if (Option.isNone(current)) {
|
|
21
|
+
return yield* new OptimisticConcurrencyException({
|
|
22
|
+
type,
|
|
23
|
+
id: e[idKey] as string,
|
|
24
|
+
current: "",
|
|
25
|
+
found: e._etag,
|
|
26
|
+
code: 409
|
|
27
|
+
})
|
|
28
|
+
}
|
|
25
29
|
}
|
|
26
30
|
if (Option.isSome(current) && current.value._etag !== e._etag) {
|
|
27
31
|
return yield* new OptimisticConcurrencyException({
|