@effect-app/infra 2.11.0 → 2.12.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 +6 -0
- package/_cjs/Model/Repository/ext.cjs +1 -1
- package/_cjs/Model/Repository/ext.cjs.map +1 -1
- package/_cjs/Model/Repository/internal/internal.cjs +18 -20
- package/_cjs/Model/Repository/internal/internal.cjs.map +1 -1
- package/_cjs/Model/Repository/makeRepo.cjs.map +1 -1
- package/_cjs/Store/Cosmos/query.cjs +9 -2
- package/_cjs/Store/Cosmos/query.cjs.map +1 -1
- package/_cjs/Store/Cosmos.cjs +49 -23
- package/_cjs/Store/Cosmos.cjs.map +1 -1
- package/_cjs/Store/Disk.cjs +5 -5
- package/_cjs/Store/Disk.cjs.map +1 -1
- package/_cjs/Store/Memory.cjs +9 -9
- package/_cjs/Store/Memory.cjs.map +1 -1
- package/_cjs/Store/codeFilter.cjs.map +1 -1
- package/_cjs/Store/service.cjs.map +1 -1
- package/_cjs/Store/utils.cjs +3 -3
- package/_cjs/Store/utils.cjs.map +1 -1
- package/dist/Model/Repository/ext.d.ts +6 -9
- package/dist/Model/Repository/ext.d.ts.map +1 -1
- package/dist/Model/Repository/ext.js +2 -2
- package/dist/Model/Repository/internal/internal.d.ts +4 -9
- package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
- package/dist/Model/Repository/internal/internal.js +19 -21
- package/dist/Model/Repository/legacy.d.ts +2 -6
- package/dist/Model/Repository/legacy.d.ts.map +1 -1
- package/dist/Model/Repository/makeRepo.d.ts +4 -9
- 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 +1 -3
- package/dist/Model/Repository/service.d.ts.map +1 -1
- package/dist/QueueMaker/sbqueue.d.ts +2 -2
- package/dist/Store/Cosmos/query.d.ts +1 -1
- package/dist/Store/Cosmos/query.d.ts.map +1 -1
- package/dist/Store/Cosmos/query.js +7 -3
- package/dist/Store/Cosmos.d.ts.map +1 -1
- package/dist/Store/Cosmos.js +43 -23
- package/dist/Store/Disk.d.ts +2 -3
- package/dist/Store/Disk.d.ts.map +1 -1
- package/dist/Store/Disk.js +6 -6
- package/dist/Store/Memory.d.ts +4 -9
- package/dist/Store/Memory.d.ts.map +1 -1
- package/dist/Store/Memory.js +12 -12
- package/dist/Store/codeFilter.d.ts +2 -3
- package/dist/Store/codeFilter.d.ts.map +1 -1
- package/dist/Store/codeFilter.js +1 -1
- package/dist/Store/service.d.ts +9 -23
- package/dist/Store/service.d.ts.map +1 -1
- package/dist/Store/service.js +1 -1
- package/dist/Store/utils.d.ts +1 -3
- package/dist/Store/utils.d.ts.map +1 -1
- package/dist/Store/utils.js +4 -4
- package/package.json +1 -1
- package/src/Model/Repository/ext.ts +6 -5
- package/src/Model/Repository/internal/internal.ts +37 -36
- package/src/Model/Repository/legacy.ts +2 -2
- package/src/Model/Repository/makeRepo.ts +8 -9
- package/src/Model/Repository/service.ts +1 -1
- package/src/Store/Cosmos/query.ts +6 -1
- package/src/Store/Cosmos.ts +85 -48
- package/src/Store/Disk.ts +20 -8
- package/src/Store/Memory.ts +25 -16
- package/src/Store/codeFilter.ts +2 -1
- package/src/Store/service.ts +8 -7
- package/src/Store/utils.ts +4 -3
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
1
2
|
import type {} from "effect/Equal"
|
|
2
3
|
import type {} from "effect/Hash"
|
|
3
4
|
import type { NonEmptyReadonlyArray } from "effect-app"
|
|
@@ -26,9 +27,9 @@ export function makeRepoInternal<
|
|
|
26
27
|
return <
|
|
27
28
|
ItemType extends string,
|
|
28
29
|
R,
|
|
29
|
-
Encoded extends
|
|
30
|
+
Encoded extends FieldValues,
|
|
30
31
|
T,
|
|
31
|
-
IdKey extends keyof T
|
|
32
|
+
IdKey extends keyof T & keyof Encoded
|
|
32
33
|
>(
|
|
33
34
|
name: ItemType,
|
|
34
35
|
schema: S.Schema<T, Encoded, R>,
|
|
@@ -41,18 +42,18 @@ export function makeRepoInternal<
|
|
|
41
42
|
e: Encoded,
|
|
42
43
|
getEtag: (id: string) => string | undefined
|
|
43
44
|
): PM {
|
|
44
|
-
return mapTo(e, getEtag(e
|
|
45
|
+
return mapTo(e, getEtag(e[idKey]))
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
function mapReverse(
|
|
48
49
|
{ _etag, ...e }: PM,
|
|
49
50
|
setEtag: (id: string, eTag: string | undefined) => void
|
|
50
51
|
): Encoded {
|
|
51
|
-
setEtag(e
|
|
52
|
+
setEtag((e as any)[idKey], _etag)
|
|
52
53
|
return mapFrom(e as unknown as Encoded)
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
const mkStore = makeStore<Encoded>()(name, schema, mapTo)
|
|
56
|
+
const mkStore = makeStore<Encoded>()(name, schema, mapTo, idKey)
|
|
56
57
|
|
|
57
58
|
function make<RInitial = never, E = never, RPublish = never, RCtx = never>(
|
|
58
59
|
args: [Evt] extends [never] ? {
|
|
@@ -128,7 +129,7 @@ export function makeRepoInternal<
|
|
|
128
129
|
: s.pipe(S.pick(idKey as any))
|
|
129
130
|
})
|
|
130
131
|
const encodeId = flow(S.encode(i), provideRctx)
|
|
131
|
-
function findEId(id: Encoded[
|
|
132
|
+
function findEId(id: Encoded[IdKey]) {
|
|
132
133
|
return Effect.flatMap(
|
|
133
134
|
store.find(id),
|
|
134
135
|
(item) =>
|
|
@@ -138,13 +139,12 @@ export function makeRepoInternal<
|
|
|
138
139
|
})
|
|
139
140
|
)
|
|
140
141
|
}
|
|
142
|
+
// TODO: select the particular field, instead of as struct
|
|
141
143
|
function findE(id: T[IdKey]) {
|
|
142
144
|
return pipe(
|
|
143
145
|
encodeId({ [idKey]: id } as any),
|
|
144
146
|
Effect.orDie,
|
|
145
|
-
|
|
146
|
-
// TODO: make reliable. (Security: isin: PrimaryKey(ISIN), idKey: "isin", does end up with "id")
|
|
147
|
-
Effect.map((_) => (_ as any)[idKey] ?? (_ as any).id),
|
|
147
|
+
Effect.map((_) => (_ as any)[idKey]),
|
|
148
148
|
Effect.flatMap(findEId)
|
|
149
149
|
)
|
|
150
150
|
}
|
|
@@ -163,7 +163,7 @@ export function makeRepoInternal<
|
|
|
163
163
|
const { get, set } = yield* cms
|
|
164
164
|
const items = a.map((_) => mapToPersistenceModel(_, get))
|
|
165
165
|
const ret = yield* store.batchSet(items)
|
|
166
|
-
ret.forEach((_) => set(_
|
|
166
|
+
ret.forEach((_) => set(_[idKey], _._etag))
|
|
167
167
|
})
|
|
168
168
|
)
|
|
169
169
|
.pipe(Effect.asVoid)
|
|
@@ -199,7 +199,7 @@ export function makeRepoInternal<
|
|
|
199
199
|
// TODO: we should have a batchRemove on store so the adapter can actually batch...
|
|
200
200
|
for (const e of items) {
|
|
201
201
|
yield* store.remove(mapToPersistenceModel(e, get))
|
|
202
|
-
set(e
|
|
202
|
+
set(e[idKey], undefined)
|
|
203
203
|
}
|
|
204
204
|
yield* Effect
|
|
205
205
|
.sync(() => toNonEmptyArray([...events]))
|
|
@@ -233,13 +233,13 @@ export function makeRepoInternal<
|
|
|
233
233
|
{
|
|
234
234
|
...args,
|
|
235
235
|
select: args.select
|
|
236
|
-
? dedupe([...args.select,
|
|
236
|
+
? dedupe([...args.select, idKey, "_etag" as any])
|
|
237
237
|
: undefined
|
|
238
238
|
} as typeof args
|
|
239
239
|
)
|
|
240
240
|
.pipe(
|
|
241
241
|
Effect.tap((items) =>
|
|
242
|
-
Effect.map(cms, ({ set }) => items.forEach((_) => set((_ as Encoded)
|
|
242
|
+
Effect.map(cms, ({ set }) => items.forEach((_) => set((_ as Encoded)[idKey], (_ as PM)._etag)))
|
|
243
243
|
)
|
|
244
244
|
)
|
|
245
245
|
|
|
@@ -372,46 +372,47 @@ const pluralize = (s: string) =>
|
|
|
372
372
|
? s.substring(0, s.length - 1) + "ies"
|
|
373
373
|
: s + "s"
|
|
374
374
|
|
|
375
|
-
export function makeStore<
|
|
376
|
-
Encoded extends { id: string }
|
|
377
|
-
>() {
|
|
375
|
+
export function makeStore<Encoded extends FieldValues>() {
|
|
378
376
|
return <
|
|
379
377
|
ItemType extends string,
|
|
380
378
|
R,
|
|
381
|
-
E
|
|
382
|
-
T
|
|
379
|
+
E,
|
|
380
|
+
T,
|
|
381
|
+
IdKey extends keyof Encoded
|
|
383
382
|
>(
|
|
384
383
|
name: ItemType,
|
|
385
384
|
schema: S.Schema<T, E, R>,
|
|
386
|
-
mapTo: (e: E, etag: string | undefined) => Encoded
|
|
385
|
+
mapTo: (e: E, etag: string | undefined) => Encoded,
|
|
386
|
+
idKey: IdKey
|
|
387
387
|
) => {
|
|
388
|
-
function encodeToEncoded() {
|
|
389
|
-
const getEtag = () => undefined
|
|
390
|
-
return (t: T) =>
|
|
391
|
-
S.encode(schema)(t).pipe(
|
|
392
|
-
Effect.orDie,
|
|
393
|
-
Effect.map((_) => mapToPersistenceModel(_, getEtag))
|
|
394
|
-
)
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
function mapToPersistenceModel(
|
|
398
|
-
e: E,
|
|
399
|
-
getEtag: (id: string) => string | undefined
|
|
400
|
-
): Encoded {
|
|
401
|
-
return mapTo(e, getEtag(e.id))
|
|
402
|
-
}
|
|
403
|
-
|
|
404
388
|
function makeStore<RInitial = never, EInitial = never>(
|
|
405
389
|
makeInitial?: Effect<readonly T[], EInitial, RInitial>,
|
|
406
390
|
config?: Omit<StoreConfig<Encoded>, "partitionValue"> & {
|
|
407
391
|
partitionValue?: (a: Encoded) => string
|
|
408
392
|
}
|
|
409
393
|
) {
|
|
394
|
+
function encodeToEncoded() {
|
|
395
|
+
const getEtag = () => undefined
|
|
396
|
+
return (t: T) =>
|
|
397
|
+
S.encode(schema)(t).pipe(
|
|
398
|
+
Effect.orDie,
|
|
399
|
+
Effect.map((_) => mapToPersistenceModel(_, getEtag))
|
|
400
|
+
)
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function mapToPersistenceModel(
|
|
404
|
+
e: E,
|
|
405
|
+
getEtag: (id: string) => string | undefined
|
|
406
|
+
): Encoded {
|
|
407
|
+
return mapTo(e, getEtag((e as any)[idKey] as string))
|
|
408
|
+
}
|
|
409
|
+
|
|
410
410
|
return Effect.gen(function*() {
|
|
411
411
|
const { make } = yield* StoreMaker
|
|
412
412
|
|
|
413
|
-
const store = yield* make<
|
|
413
|
+
const store = yield* make<IdKey, Encoded, RInitial | R, EInitial>(
|
|
414
414
|
pluralize(name),
|
|
415
|
+
idKey,
|
|
415
416
|
makeInitial
|
|
416
417
|
? makeInitial
|
|
417
418
|
.pipe(
|
|
@@ -14,13 +14,13 @@ export interface Mapped2<A, R> {
|
|
|
14
14
|
all: Effect<A[], ParseResult.ParseError, R>
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
export interface Mapped<Encoded
|
|
17
|
+
export interface Mapped<Encoded> {
|
|
18
18
|
<A, R, IdKey extends keyof A>(schema: S.Schema<A, Encoded, R>): Mapped1<A, IdKey, R>
|
|
19
19
|
// TODO: constrain on Encoded2 having to contain only fields that fit Encoded
|
|
20
20
|
<A, Encoded2, R>(schema: S.Schema<A, Encoded2, R>): Mapped2<A, R>
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
export interface MM<Repo, Encoded
|
|
23
|
+
export interface MM<Repo, Encoded> {
|
|
24
24
|
<A, R, IdKey extends keyof A>(schema: S.Schema<A, Encoded, R>): Effect<Mapped1<A, IdKey, R>, never, Repo>
|
|
25
25
|
// TODO: constrain on Encoded2 having to contain only fields that fit Encoded
|
|
26
26
|
<A, Encoded2, R>(schema: S.Schema<A, Encoded2, R>): Effect<Mapped2<A, R>, never, Repo>
|
|
@@ -10,15 +10,14 @@ import type {} from "effect/Hash"
|
|
|
10
10
|
import type { Context, NonEmptyReadonlyArray, S } from "effect-app"
|
|
11
11
|
import { Effect } from "effect-app"
|
|
12
12
|
import type { StoreConfig, StoreMaker } from "../../Store.js"
|
|
13
|
+
import type { FieldValues } from "../filter/types.js"
|
|
13
14
|
import type { ExtendedRepository } from "./ext.js"
|
|
14
15
|
import { extendRepo } from "./ext.js"
|
|
15
16
|
import { makeRepoInternal } from "./internal/internal.js"
|
|
16
17
|
|
|
17
18
|
export interface RepositoryOptions<
|
|
18
|
-
IdKey extends keyof T,
|
|
19
|
-
Encoded
|
|
20
|
-
id: string
|
|
21
|
-
},
|
|
19
|
+
IdKey extends keyof T & keyof Encoded,
|
|
20
|
+
Encoded,
|
|
22
21
|
T,
|
|
23
22
|
Evt = never,
|
|
24
23
|
RPublish = never,
|
|
@@ -66,9 +65,9 @@ export const makeRepo: {
|
|
|
66
65
|
<
|
|
67
66
|
ItemType extends string,
|
|
68
67
|
RSchema,
|
|
69
|
-
Encoded extends
|
|
68
|
+
Encoded extends FieldValues,
|
|
70
69
|
T,
|
|
71
|
-
IdKey extends keyof T,
|
|
70
|
+
IdKey extends keyof T & keyof Encoded,
|
|
72
71
|
E = never,
|
|
73
72
|
Evt = never,
|
|
74
73
|
RInitial = never,
|
|
@@ -86,7 +85,7 @@ export const makeRepo: {
|
|
|
86
85
|
<
|
|
87
86
|
ItemType extends string,
|
|
88
87
|
RSchema,
|
|
89
|
-
Encoded extends
|
|
88
|
+
Encoded extends FieldValues,
|
|
90
89
|
T extends { id: unknown },
|
|
91
90
|
E = never,
|
|
92
91
|
Evt = never,
|
|
@@ -105,9 +104,9 @@ export const makeRepo: {
|
|
|
105
104
|
} = <
|
|
106
105
|
ItemType extends string,
|
|
107
106
|
R,
|
|
108
|
-
Encoded extends
|
|
107
|
+
Encoded extends FieldValues,
|
|
109
108
|
T,
|
|
110
|
-
IdKey extends keyof T,
|
|
109
|
+
IdKey extends keyof T & keyof Encoded,
|
|
111
110
|
E = never,
|
|
112
111
|
RInitial = never,
|
|
113
112
|
RPublish = never,
|
|
@@ -32,6 +32,7 @@ const arrayContains = (v: any[]) => v.map((_) => JSON.stringify(_)).join(", ")
|
|
|
32
32
|
const vAsArr = (v: string) => v as unknown as any[]
|
|
33
33
|
|
|
34
34
|
export function buildWhereCosmosQuery3(
|
|
35
|
+
idKey: PropertyKey,
|
|
35
36
|
filter: readonly FilterResult[],
|
|
36
37
|
name: string,
|
|
37
38
|
importedMarkerId: string,
|
|
@@ -42,10 +43,14 @@ export function buildWhereCosmosQuery3(
|
|
|
42
43
|
limit?: number
|
|
43
44
|
) {
|
|
44
45
|
const statement = (x: FilterR, i: number) => {
|
|
46
|
+
if (x.path === idKey) {
|
|
47
|
+
x = { ...x, path: "id" }
|
|
48
|
+
}
|
|
45
49
|
let k = x.path.includes(".-1.")
|
|
46
50
|
? `${x.path.split(".-1.")[0]}.${x.path.split(".-1.")[1]!}`
|
|
47
51
|
: `f.${x.path}`
|
|
48
52
|
|
|
53
|
+
// would have to map id, but shouldnt allow id in defaultValues anyway..
|
|
49
54
|
k = x.path in defaultValues ? `(${k} ?? ${JSON.stringify(defaultValues[x.path])})` : k
|
|
50
55
|
|
|
51
56
|
const v = "@v" + i
|
|
@@ -180,7 +185,7 @@ export function buildWhereCosmosQuery3(
|
|
|
180
185
|
query: `
|
|
181
186
|
SELECT ${
|
|
182
187
|
select
|
|
183
|
-
? `${select.map((_) => `f.${_}`).join(", ")}`
|
|
188
|
+
? `${select.map((_) => (_ as any) === idKey ? "id" : _).map((_) => `f.${_}`).join(", ")}`
|
|
184
189
|
: "f"
|
|
185
190
|
}
|
|
186
191
|
FROM ${name} f
|
package/src/Store/Cosmos.ts
CHANGED
|
@@ -7,10 +7,21 @@ import { dropUndefinedT } from "effect-app/utils"
|
|
|
7
7
|
import { CosmosClient, CosmosClientLayer } from "../adapters/cosmos-client.js"
|
|
8
8
|
import { OptimisticConcurrencyException } from "../errors.js"
|
|
9
9
|
import { InfraLogger } from "../logger.js"
|
|
10
|
+
import type { FieldValues } from "../Model/filter/types.js"
|
|
10
11
|
import { buildWhereCosmosQuery3, logQuery } from "./Cosmos/query.js"
|
|
11
12
|
import { StoreMaker } from "./service.js"
|
|
12
13
|
import type { FilterArgs, PersistenceModelType, StorageConfig, Store, StoreConfig } from "./service.js"
|
|
13
14
|
|
|
15
|
+
const makeMapId =
|
|
16
|
+
<IdKey extends keyof Encoded, Encoded extends FieldValues>(idKey: IdKey) => ({ [idKey]: id, ...e }: Encoded) => ({
|
|
17
|
+
...e,
|
|
18
|
+
id
|
|
19
|
+
})
|
|
20
|
+
const makeReverseMapId =
|
|
21
|
+
<IdKey extends keyof Encoded, Encoded extends FieldValues>(idKey: IdKey) =>
|
|
22
|
+
({ id, ...t }: PersistenceModelType<Omit<Encoded, IdKey> & { id: string }>) =>
|
|
23
|
+
({ ...t, [idKey]: id }) as any as PersistenceModelType<Encoded>
|
|
24
|
+
|
|
14
25
|
class CosmosDbOperationError {
|
|
15
26
|
constructor(readonly message: string) {}
|
|
16
27
|
} // TODO: Retry operation when running into RU limit.
|
|
@@ -19,13 +30,17 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
19
30
|
return Effect.gen(function*() {
|
|
20
31
|
const { db } = yield* CosmosClient
|
|
21
32
|
return {
|
|
22
|
-
make: <
|
|
33
|
+
make: <IdKey extends keyof Encoded, Encoded extends FieldValues, R = never, E = never>(
|
|
23
34
|
name: string,
|
|
35
|
+
idKey: IdKey,
|
|
24
36
|
seed?: Effect<Iterable<Encoded>, E, R>,
|
|
25
37
|
config?: StoreConfig<Encoded>
|
|
26
38
|
) =>
|
|
27
39
|
Effect.gen(function*() {
|
|
40
|
+
const mapId = makeMapId<IdKey, Encoded>(idKey)
|
|
41
|
+
const mapReverseId = makeReverseMapId<IdKey, Encoded>(idKey)
|
|
28
42
|
type PM = PersistenceModelType<Encoded>
|
|
43
|
+
type PMCosmos = PersistenceModelType<Omit<Encoded, IdKey> & { id: string }>
|
|
29
44
|
const containerId = `${prefix}${name}`
|
|
30
45
|
yield* Effect.promise(() =>
|
|
31
46
|
db.containers.createIfNotExists(dropUndefinedT({
|
|
@@ -47,34 +62,37 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
47
62
|
.gen(function*() {
|
|
48
63
|
// TODO: disable batching if need atomicity
|
|
49
64
|
// we delay and batch to keep low amount of RUs
|
|
50
|
-
const b = [...items]
|
|
51
|
-
(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
65
|
+
const b = [...items]
|
|
66
|
+
.map(
|
|
67
|
+
(x) =>
|
|
68
|
+
[
|
|
69
|
+
x,
|
|
70
|
+
Option.match(Option.fromNullable(x._etag), {
|
|
71
|
+
onNone: () =>
|
|
72
|
+
dropUndefinedT({
|
|
73
|
+
operationType: "Create" as const,
|
|
74
|
+
resourceBody: {
|
|
75
|
+
...Struct.omit(x, "_etag", idKey),
|
|
76
|
+
id: x[idKey],
|
|
77
|
+
_partitionKey: config?.partitionValue(x)
|
|
78
|
+
},
|
|
79
|
+
partitionKey: config?.partitionValue(x)
|
|
80
|
+
}),
|
|
81
|
+
onSome: (eTag) =>
|
|
82
|
+
dropUndefinedT({
|
|
83
|
+
operationType: "Replace" as const,
|
|
84
|
+
id: x[idKey],
|
|
85
|
+
resourceBody: {
|
|
86
|
+
...Struct.omit(x, "_etag", idKey),
|
|
87
|
+
id: x[idKey],
|
|
88
|
+
_partitionKey: config?.partitionValue(x)
|
|
89
|
+
},
|
|
90
|
+
ifMatch: eTag,
|
|
91
|
+
partitionKey: config?.partitionValue(x)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
] as const
|
|
95
|
+
)
|
|
78
96
|
const batches = Chunk.toReadonlyArray(Array.chunk_(b, config?.maxBulkSize ?? 10))
|
|
79
97
|
|
|
80
98
|
const batchResult = yield* Effect.forEach(
|
|
@@ -135,15 +153,17 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
135
153
|
onNone: () => ({
|
|
136
154
|
operationType: "Create" as const,
|
|
137
155
|
resourceBody: {
|
|
138
|
-
...Struct.omit(x, "_etag"),
|
|
156
|
+
...Struct.omit(x, "_etag", idKey),
|
|
157
|
+
id: x[idKey],
|
|
139
158
|
_partitionKey: config?.partitionValue(x)
|
|
140
159
|
}
|
|
141
160
|
}),
|
|
142
161
|
onSome: (eTag) => ({
|
|
143
162
|
operationType: "Replace" as const,
|
|
144
|
-
id: x
|
|
163
|
+
id: x[idKey],
|
|
145
164
|
resourceBody: {
|
|
146
|
-
...Struct.omit(x, "_etag"),
|
|
165
|
+
...Struct.omit(x, "_etag", idKey),
|
|
166
|
+
id: x[idKey],
|
|
147
167
|
_partitionKey: config?.partitionValue(x)
|
|
148
168
|
},
|
|
149
169
|
ifMatch: eTag
|
|
@@ -173,8 +193,9 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
173
193
|
)
|
|
174
194
|
}
|
|
175
195
|
|
|
176
|
-
return batch.map(([e], i) => ({
|
|
196
|
+
return batch.map(([{ id, ...e }], i) => ({
|
|
177
197
|
...e,
|
|
198
|
+
[idKey]: id,
|
|
178
199
|
_etag: result[i]?.eTag
|
|
179
200
|
})) as unknown as NonEmptyReadonlyArray<Encoded>
|
|
180
201
|
})
|
|
@@ -187,7 +208,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
187
208
|
}))
|
|
188
209
|
}
|
|
189
210
|
|
|
190
|
-
const s: Store<
|
|
211
|
+
const s: Store<IdKey, Encoded> = {
|
|
191
212
|
all: Effect
|
|
192
213
|
.sync(() => ({
|
|
193
214
|
query: `SELECT * FROM ${name} f WHERE f.id != @id`,
|
|
@@ -199,9 +220,13 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
199
220
|
Effect.promise(() =>
|
|
200
221
|
container
|
|
201
222
|
.items
|
|
202
|
-
.query<
|
|
223
|
+
.query<PMCosmos>(q)
|
|
203
224
|
.fetchAll()
|
|
204
|
-
.then(({ resources }) =>
|
|
225
|
+
.then(({ resources }) =>
|
|
226
|
+
resources.map(
|
|
227
|
+
(_) => ({ ...defaultValues, ...mapReverseId(_) })
|
|
228
|
+
)
|
|
229
|
+
)
|
|
205
230
|
)
|
|
206
231
|
),
|
|
207
232
|
Effect
|
|
@@ -223,6 +248,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
223
248
|
return Effect
|
|
224
249
|
.sync(() =>
|
|
225
250
|
buildWhereCosmosQuery3(
|
|
251
|
+
idKey,
|
|
226
252
|
filter ?? [],
|
|
227
253
|
name,
|
|
228
254
|
importedMarkerId,
|
|
@@ -244,13 +270,20 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
244
270
|
.query<M>(q)
|
|
245
271
|
.fetchAll()
|
|
246
272
|
.then(({ resources }) =>
|
|
247
|
-
resources.map((_) =>
|
|
273
|
+
resources.map((_) =>
|
|
274
|
+
({
|
|
275
|
+
...pipe(defaultValues, Struct.pick(...f.select!)),
|
|
276
|
+
...mapReverseId(_ as any)
|
|
277
|
+
}) as any
|
|
278
|
+
)
|
|
248
279
|
)
|
|
249
280
|
: container
|
|
250
281
|
.items
|
|
251
282
|
.query<{ f: M }>(q)
|
|
252
283
|
.fetchAll()
|
|
253
|
-
.then(({ resources }) =>
|
|
284
|
+
.then(({ resources }) =>
|
|
285
|
+
resources.map(({ f }) => ({ ...defaultValues, ...mapReverseId(f as any) }) as any)
|
|
286
|
+
)
|
|
254
287
|
)
|
|
255
288
|
)
|
|
256
289
|
)
|
|
@@ -263,10 +296,10 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
263
296
|
Effect
|
|
264
297
|
.promise(() =>
|
|
265
298
|
container
|
|
266
|
-
.item(id, config?.partitionValue({ id } as Encoded))
|
|
299
|
+
.item(id, config?.partitionValue({ [idKey]: id } as Encoded))
|
|
267
300
|
.read<Encoded>()
|
|
268
301
|
.then(({ resource }) =>
|
|
269
|
-
Option.fromNullable(resource).pipe(Option.map((_) => ({ ...defaultValues, ..._ })))
|
|
302
|
+
Option.fromNullable(resource).pipe(Option.map((_) => ({ ...defaultValues, ...mapReverseId(_) })))
|
|
270
303
|
)
|
|
271
304
|
)
|
|
272
305
|
.pipe(Effect
|
|
@@ -275,7 +308,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
275
308
|
attributes: {
|
|
276
309
|
"repository.container_id": containerId,
|
|
277
310
|
"repository.model_name": name,
|
|
278
|
-
partitionValue: config?.partitionValue({ id } as Encoded),
|
|
311
|
+
partitionValue: config?.partitionValue({ [idKey]: id } as Encoded),
|
|
279
312
|
id
|
|
280
313
|
}
|
|
281
314
|
})),
|
|
@@ -288,14 +321,14 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
288
321
|
onNone: () =>
|
|
289
322
|
Effect.promise(() =>
|
|
290
323
|
container.items.create({
|
|
291
|
-
...e,
|
|
324
|
+
...mapId(e),
|
|
292
325
|
_partitionKey: config?.partitionValue(e)
|
|
293
326
|
})
|
|
294
327
|
),
|
|
295
328
|
onSome: (eTag) =>
|
|
296
329
|
Effect.promise(() =>
|
|
297
|
-
container.item(e
|
|
298
|
-
{ ...e, _partitionKey: config?.partitionValue(e) },
|
|
330
|
+
container.item(e[idKey], config?.partitionValue(e)).replace(
|
|
331
|
+
{ ...mapId(e), _partitionKey: config?.partitionValue(e) },
|
|
299
332
|
{
|
|
300
333
|
accessCondition: {
|
|
301
334
|
type: "IfMatch",
|
|
@@ -310,7 +343,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
310
343
|
Effect
|
|
311
344
|
.flatMap((x) => {
|
|
312
345
|
if (x.statusCode === 412 || x.statusCode === 404) {
|
|
313
|
-
return new OptimisticConcurrencyException({ type: name, id: e
|
|
346
|
+
return new OptimisticConcurrencyException({ type: name, id: e[idKey] })
|
|
314
347
|
}
|
|
315
348
|
if (x.statusCode > 299 || x.statusCode < 200) {
|
|
316
349
|
return Effect.die(
|
|
@@ -327,18 +360,22 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
327
360
|
Effect
|
|
328
361
|
.withSpan("Cosmos.set [effect-app/infra/Store]", {
|
|
329
362
|
captureStackTrace: false,
|
|
330
|
-
attributes: {
|
|
363
|
+
attributes: {
|
|
364
|
+
"repository.container_id": containerId,
|
|
365
|
+
"repository.model_name": name,
|
|
366
|
+
id: e[idKey]
|
|
367
|
+
}
|
|
331
368
|
})
|
|
332
369
|
),
|
|
333
370
|
batchSet,
|
|
334
371
|
bulkSet,
|
|
335
372
|
remove: (e: Encoded) =>
|
|
336
373
|
Effect
|
|
337
|
-
.promise(() => container.item(e
|
|
374
|
+
.promise(() => container.item(e[idKey], config?.partitionValue(e)).delete())
|
|
338
375
|
.pipe(Effect
|
|
339
376
|
.withSpan("Cosmos.remove [effect-app/infra/Store]", {
|
|
340
377
|
captureStackTrace: false,
|
|
341
|
-
attributes: { "repository.container_id": containerId, "repository.model_name": name, id: e
|
|
378
|
+
attributes: { "repository.container_id": containerId, "repository.model_name": name, id: e[idKey] }
|
|
342
379
|
}))
|
|
343
380
|
}
|
|
344
381
|
|
package/src/Store/Disk.ts
CHANGED
|
@@ -4,12 +4,14 @@ import * as fu from "../fileUtil.js"
|
|
|
4
4
|
import fs from "fs"
|
|
5
5
|
|
|
6
6
|
import { Console, Effect, FiberRef, flow } from "effect-app"
|
|
7
|
+
import type { FieldValues } from "../Model/filter/types.js"
|
|
7
8
|
import { makeMemoryStoreInt, storeId } from "./Memory.js"
|
|
8
9
|
import type { PersistenceModelType, StorageConfig, Store, StoreConfig } from "./service.js"
|
|
9
10
|
import { StoreMaker } from "./service.js"
|
|
10
11
|
|
|
11
|
-
function makeDiskStoreInt<
|
|
12
|
+
function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValues, R, E>(
|
|
12
13
|
prefix: string,
|
|
14
|
+
idKey: IdKey,
|
|
13
15
|
namespace: string,
|
|
14
16
|
dir: string,
|
|
15
17
|
name: string,
|
|
@@ -68,8 +70,9 @@ function makeDiskStoreInt<Id extends string, Encoded extends { id: Id }, R, E>(
|
|
|
68
70
|
)
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
const store = yield* makeMemoryStoreInt<
|
|
73
|
+
const store = yield* makeMemoryStoreInt<IdKey, Encoded, R, E>(
|
|
72
74
|
name,
|
|
75
|
+
idKey,
|
|
73
76
|
namespace,
|
|
74
77
|
!fs.existsSync(file)
|
|
75
78
|
? seed
|
|
@@ -107,7 +110,7 @@ function makeDiskStoreInt<Id extends string, Encoded extends { id: Id }, R, E>(
|
|
|
107
110
|
store.remove,
|
|
108
111
|
Effect.tap(flushToDiskInBackground)
|
|
109
112
|
)
|
|
110
|
-
} satisfies Store<
|
|
113
|
+
} satisfies Store<IdKey, Encoded>
|
|
111
114
|
})
|
|
112
115
|
}
|
|
113
116
|
|
|
@@ -121,15 +124,16 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
|
|
|
121
124
|
fs.mkdirSync(dir)
|
|
122
125
|
}
|
|
123
126
|
return {
|
|
124
|
-
make: <
|
|
127
|
+
make: <IdKey extends keyof Encoded, Encoded extends FieldValues, R, E>(
|
|
125
128
|
name: string,
|
|
129
|
+
idKey: IdKey,
|
|
126
130
|
seed?: Effect<Iterable<Encoded>, E, R>,
|
|
127
131
|
config?: StoreConfig<Encoded>
|
|
128
132
|
) =>
|
|
129
133
|
Effect.gen(function*() {
|
|
130
134
|
const storesSem = Effect.unsafeMakeSemaphore(1)
|
|
131
|
-
const primary = yield* makeDiskStoreInt(prefix, "primary", dir, name, seed, config?.defaultValues)
|
|
132
|
-
const stores = new Map<string, Store<
|
|
135
|
+
const primary = yield* makeDiskStoreInt(prefix, idKey, "primary", dir, name, seed, config?.defaultValues)
|
|
136
|
+
const stores = new Map<string, Store<IdKey, Encoded>>([["primary", primary]])
|
|
133
137
|
const ctx = yield* Effect.context<R>()
|
|
134
138
|
const getStore = !config?.allowNamespace
|
|
135
139
|
? Effect.succeed(primary)
|
|
@@ -145,7 +149,15 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
|
|
|
145
149
|
Effect.suspend(() => {
|
|
146
150
|
const existing = stores.get(namespace)
|
|
147
151
|
if (existing) return Effect.sync(() => existing)
|
|
148
|
-
return makeDiskStoreInt<
|
|
152
|
+
return makeDiskStoreInt<IdKey, Encoded, R, E>(
|
|
153
|
+
prefix,
|
|
154
|
+
idKey,
|
|
155
|
+
namespace,
|
|
156
|
+
dir,
|
|
157
|
+
name,
|
|
158
|
+
seed,
|
|
159
|
+
config?.defaultValues
|
|
160
|
+
)
|
|
149
161
|
.pipe(
|
|
150
162
|
Effect.orDie,
|
|
151
163
|
Effect.provide(ctx),
|
|
@@ -155,7 +167,7 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
|
|
|
155
167
|
)
|
|
156
168
|
}))
|
|
157
169
|
|
|
158
|
-
const s: Store<
|
|
170
|
+
const s: Store<IdKey, Encoded> = {
|
|
159
171
|
all: Effect.flatMap(getStore, (_) => _.all),
|
|
160
172
|
find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
|
|
161
173
|
filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
|