@effect-app/infra 2.11.0 → 2.12.1
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 +12 -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 +44 -22
- 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 +3 -6
- 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 +5 -10
- 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/Operations.d.ts +3 -3
- 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 +41 -22
- 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 +8 -22
- 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 +83 -47
- 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
- package/vitest.config.ts.timestamp-1711656440838-19c636fe320df.mjs +0 -0
- package/vitest.config.ts.timestamp-1711724061890-6ecedb0a07fdd.mjs +0 -0
- package/vitest.config.ts.timestamp-1711743489537-da8d9e5f66c9f.mjs +0 -0
- package/vitest.config.ts.timestamp-1711744615239-dcf257a844e01.mjs +0 -37
|
@@ -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
|
|
@@ -187,7 +207,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
187
207
|
}))
|
|
188
208
|
}
|
|
189
209
|
|
|
190
|
-
const s: Store<
|
|
210
|
+
const s: Store<IdKey, Encoded> = {
|
|
191
211
|
all: Effect
|
|
192
212
|
.sync(() => ({
|
|
193
213
|
query: `SELECT * FROM ${name} f WHERE f.id != @id`,
|
|
@@ -199,9 +219,13 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
199
219
|
Effect.promise(() =>
|
|
200
220
|
container
|
|
201
221
|
.items
|
|
202
|
-
.query<
|
|
222
|
+
.query<PMCosmos>(q)
|
|
203
223
|
.fetchAll()
|
|
204
|
-
.then(({ resources }) =>
|
|
224
|
+
.then(({ resources }) =>
|
|
225
|
+
resources.map(
|
|
226
|
+
(_) => ({ ...defaultValues, ...mapReverseId(_) })
|
|
227
|
+
)
|
|
228
|
+
)
|
|
205
229
|
)
|
|
206
230
|
),
|
|
207
231
|
Effect
|
|
@@ -223,6 +247,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
223
247
|
return Effect
|
|
224
248
|
.sync(() =>
|
|
225
249
|
buildWhereCosmosQuery3(
|
|
250
|
+
idKey,
|
|
226
251
|
filter ?? [],
|
|
227
252
|
name,
|
|
228
253
|
importedMarkerId,
|
|
@@ -244,13 +269,20 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
244
269
|
.query<M>(q)
|
|
245
270
|
.fetchAll()
|
|
246
271
|
.then(({ resources }) =>
|
|
247
|
-
resources.map((_) =>
|
|
272
|
+
resources.map((_) =>
|
|
273
|
+
({
|
|
274
|
+
...pipe(defaultValues, Struct.pick(...f.select!)),
|
|
275
|
+
...mapReverseId(_ as any)
|
|
276
|
+
}) as any
|
|
277
|
+
)
|
|
248
278
|
)
|
|
249
279
|
: container
|
|
250
280
|
.items
|
|
251
281
|
.query<{ f: M }>(q)
|
|
252
282
|
.fetchAll()
|
|
253
|
-
.then(({ resources }) =>
|
|
283
|
+
.then(({ resources }) =>
|
|
284
|
+
resources.map(({ f }) => ({ ...defaultValues, ...mapReverseId(f as any) }) as any)
|
|
285
|
+
)
|
|
254
286
|
)
|
|
255
287
|
)
|
|
256
288
|
)
|
|
@@ -263,10 +295,10 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
263
295
|
Effect
|
|
264
296
|
.promise(() =>
|
|
265
297
|
container
|
|
266
|
-
.item(id, config?.partitionValue({ id } as Encoded))
|
|
298
|
+
.item(id, config?.partitionValue({ [idKey]: id } as Encoded))
|
|
267
299
|
.read<Encoded>()
|
|
268
300
|
.then(({ resource }) =>
|
|
269
|
-
Option.fromNullable(resource).pipe(Option.map((_) => ({ ...defaultValues, ..._ })))
|
|
301
|
+
Option.fromNullable(resource).pipe(Option.map((_) => ({ ...defaultValues, ...mapReverseId(_) })))
|
|
270
302
|
)
|
|
271
303
|
)
|
|
272
304
|
.pipe(Effect
|
|
@@ -275,7 +307,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
275
307
|
attributes: {
|
|
276
308
|
"repository.container_id": containerId,
|
|
277
309
|
"repository.model_name": name,
|
|
278
|
-
partitionValue: config?.partitionValue({ id } as Encoded),
|
|
310
|
+
partitionValue: config?.partitionValue({ [idKey]: id } as Encoded),
|
|
279
311
|
id
|
|
280
312
|
}
|
|
281
313
|
})),
|
|
@@ -288,14 +320,14 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
288
320
|
onNone: () =>
|
|
289
321
|
Effect.promise(() =>
|
|
290
322
|
container.items.create({
|
|
291
|
-
...e,
|
|
323
|
+
...mapId(e),
|
|
292
324
|
_partitionKey: config?.partitionValue(e)
|
|
293
325
|
})
|
|
294
326
|
),
|
|
295
327
|
onSome: (eTag) =>
|
|
296
328
|
Effect.promise(() =>
|
|
297
|
-
container.item(e
|
|
298
|
-
{ ...e, _partitionKey: config?.partitionValue(e) },
|
|
329
|
+
container.item(e[idKey], config?.partitionValue(e)).replace(
|
|
330
|
+
{ ...mapId(e), _partitionKey: config?.partitionValue(e) },
|
|
299
331
|
{
|
|
300
332
|
accessCondition: {
|
|
301
333
|
type: "IfMatch",
|
|
@@ -310,7 +342,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
310
342
|
Effect
|
|
311
343
|
.flatMap((x) => {
|
|
312
344
|
if (x.statusCode === 412 || x.statusCode === 404) {
|
|
313
|
-
return new OptimisticConcurrencyException({ type: name, id: e
|
|
345
|
+
return new OptimisticConcurrencyException({ type: name, id: e[idKey] })
|
|
314
346
|
}
|
|
315
347
|
if (x.statusCode > 299 || x.statusCode < 200) {
|
|
316
348
|
return Effect.die(
|
|
@@ -327,18 +359,22 @@ function makeCosmosStore({ prefix }: StorageConfig) {
|
|
|
327
359
|
Effect
|
|
328
360
|
.withSpan("Cosmos.set [effect-app/infra/Store]", {
|
|
329
361
|
captureStackTrace: false,
|
|
330
|
-
attributes: {
|
|
362
|
+
attributes: {
|
|
363
|
+
"repository.container_id": containerId,
|
|
364
|
+
"repository.model_name": name,
|
|
365
|
+
id: e[idKey]
|
|
366
|
+
}
|
|
331
367
|
})
|
|
332
368
|
),
|
|
333
369
|
batchSet,
|
|
334
370
|
bulkSet,
|
|
335
371
|
remove: (e: Encoded) =>
|
|
336
372
|
Effect
|
|
337
|
-
.promise(() => container.item(e
|
|
373
|
+
.promise(() => container.item(e[idKey], config?.partitionValue(e)).delete())
|
|
338
374
|
.pipe(Effect
|
|
339
375
|
.withSpan("Cosmos.remove [effect-app/infra/Store]", {
|
|
340
376
|
captureStackTrace: false,
|
|
341
|
-
attributes: { "repository.container_id": containerId, "repository.model_name": name, id: e
|
|
377
|
+
attributes: { "repository.container_id": containerId, "repository.model_name": name, id: e[idKey] }
|
|
342
378
|
}))
|
|
343
379
|
}
|
|
344
380
|
|
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)),
|