@effect-app/infra 0.221.0 → 0.223.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.
Files changed (50) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/_cjs/services/Operations.cjs +3 -2
  3. package/_cjs/services/Operations.cjs.map +1 -1
  4. package/_cjs/services/Repository/ext.cjs.map +1 -1
  5. package/_cjs/services/RepositoryBase.cjs +6 -4
  6. package/_cjs/services/RepositoryBase.cjs.map +1 -1
  7. package/_cjs/services/Store/Cosmos.cjs.map +1 -1
  8. package/_cjs/services/Store/Disk.cjs.map +1 -1
  9. package/_cjs/services/Store/Memory.cjs +1 -0
  10. package/_cjs/services/Store/Memory.cjs.map +1 -1
  11. package/_cjs/services/Store/service.cjs.map +1 -1
  12. package/_cjs/services/Store/utils.cjs.map +1 -1
  13. package/dist/services/Operations.d.ts +1 -0
  14. package/dist/services/Operations.d.ts.map +1 -1
  15. package/dist/services/Operations.js +4 -3
  16. package/dist/services/Repository/ext.d.ts +42 -15
  17. package/dist/services/Repository/ext.d.ts.map +1 -1
  18. package/dist/services/Repository/ext.js +1 -1
  19. package/dist/services/Repository/service.d.ts +3 -2
  20. package/dist/services/Repository/service.d.ts.map +1 -1
  21. package/dist/services/RepositoryBase.d.ts +81 -86
  22. package/dist/services/RepositoryBase.d.ts.map +1 -1
  23. package/dist/services/RepositoryBase.js +11 -7
  24. package/dist/services/Store/Cosmos.d.ts.map +1 -1
  25. package/dist/services/Store/Cosmos.js +1 -1
  26. package/dist/services/Store/Disk.d.ts +3 -1
  27. package/dist/services/Store/Disk.d.ts.map +1 -1
  28. package/dist/services/Store/Disk.js +1 -1
  29. package/dist/services/Store/Memory.d.ts +9 -3
  30. package/dist/services/Store/Memory.d.ts.map +1 -1
  31. package/dist/services/Store/Memory.js +2 -2
  32. package/dist/services/Store/service.d.ts +37 -18
  33. package/dist/services/Store/service.d.ts.map +1 -1
  34. package/dist/services/Store/service.js +1 -1
  35. package/dist/services/Store/utils.d.ts +6 -2
  36. package/dist/services/Store/utils.d.ts.map +1 -1
  37. package/dist/services/Store/utils.js +1 -1
  38. package/dist/services/query/new-kid-interpreter.d.ts +1 -1
  39. package/package.json +10 -10
  40. package/src/services/Operations.ts +4 -2
  41. package/src/services/Repository/ext.ts +28 -29
  42. package/src/services/Repository/service.ts +2 -3
  43. package/src/services/RepositoryBase.ts +177 -169
  44. package/src/services/Store/Cosmos.ts +13 -12
  45. package/src/services/Store/Disk.ts +12 -11
  46. package/src/services/Store/Memory.ts +13 -12
  47. package/src/services/Store/index.test.ts.bak +3 -3
  48. package/src/services/Store/service.ts +22 -19
  49. package/src/services/Store/utils.ts +2 -2
  50. package/test/query.test.ts +1 -1
@@ -18,12 +18,13 @@ function makeCosmosStore({ prefix }: StorageConfig) {
18
18
  return Effect.gen(function*($) {
19
19
  const { db } = yield* $(CosmosClient)
20
20
  return {
21
- make: <Id extends string, PM extends PersistenceModelType<Id>, R = never, E = never>(
21
+ make: <Id extends string, Encoded extends { id: Id }, R = never, E = never>(
22
22
  name: string,
23
- seed?: Effect<Iterable<PM>, E, R>,
24
- config?: StoreConfig<PM>
23
+ seed?: Effect<Iterable<Encoded>, E, R>,
24
+ config?: StoreConfig<Encoded>
25
25
  ) =>
26
26
  Effect.gen(function*($) {
27
+ type PM = PersistenceModelType<Encoded>
27
28
  const containerId = `${prefix}${name}`
28
29
  yield* $(
29
30
  Effect.promise(() =>
@@ -121,7 +122,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
121
122
  )
122
123
  )
123
124
  )
124
- return batchResult.flat() as unknown as NonEmptyReadonlyArray<PM>
125
+ return batchResult.flat() as unknown as NonEmptyReadonlyArray<Encoded>
125
126
  })
126
127
  .pipe(Effect.withSpan("Cosmos.bulkSet [effect-app/infra/Store]", {
127
128
  attributes: { "repository.container_id": containerId, "repository.model_name": name }
@@ -183,7 +184,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
183
184
  return batch.map(([e], i) => ({
184
185
  ...e,
185
186
  _etag: result[i]?.eTag
186
- })) as unknown as NonEmptyReadonlyArray<PM>
187
+ })) as unknown as NonEmptyReadonlyArray<Encoded>
187
188
  })
188
189
  ))
189
190
  })
@@ -193,7 +194,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
193
194
  }))
194
195
  }
195
196
 
196
- const s: Store<PM, Id> = {
197
+ const s: Store<Encoded, Id> = {
197
198
  all: Effect
198
199
  .sync(() => ({
199
200
  query: `SELECT * FROM ${name} f WHERE f.id != @id`,
@@ -218,13 +219,13 @@ function makeCosmosStore({ prefix }: StorageConfig) {
218
219
  /**
219
220
  * May return duplicate results for "join_find", when matching more than once.
220
221
  */
221
- filter: <U extends keyof PM = never>(
222
- f: FilterArgs<PM, U>
222
+ filter: <U extends keyof Encoded = never>(
223
+ f: FilterArgs<Encoded, U>
223
224
  ) => {
224
225
  const skip = f?.skip
225
226
  const limit = f?.limit
226
227
  const filter = f.filter ?? { type: "new-kid", build: () => [] }
227
- type M = U extends undefined ? PM : Pick<PM, U>
228
+ type M = U extends undefined ? Encoded : Pick<Encoded, U>
228
229
  return Effect
229
230
  .sync(() =>
230
231
  buildWhereCosmosQuery3(
@@ -267,8 +268,8 @@ function makeCosmosStore({ prefix }: StorageConfig) {
267
268
  Effect
268
269
  .promise(() =>
269
270
  container
270
- .item(id, config?.partitionValue({ id } as PM))
271
- .read<PM>()
271
+ .item(id, config?.partitionValue({ id } as Encoded))
272
+ .read<Encoded>()
272
273
  .then(({ resource }) =>
273
274
  Option.fromNullable(resource).pipe(Option.map((_) => ({ ...defaultValues, ..._ })))
274
275
  )
@@ -329,7 +330,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
329
330
  ),
330
331
  batchSet,
331
332
  bulkSet,
332
- remove: (e: PM) =>
333
+ remove: (e: Encoded) =>
333
334
  Effect
334
335
  .promise(() => container.item(e.id, config?.partitionValue(e)).delete())
335
336
  .pipe(Effect
@@ -8,14 +8,15 @@ import { makeMemoryStoreInt, storeId } from "./Memory.js"
8
8
  import type { PersistenceModelType, StorageConfig, Store, StoreConfig } from "./service.js"
9
9
  import { StoreMaker } from "./service.js"
10
10
 
11
- function makeDiskStoreInt<Id extends string, PM extends PersistenceModelType<Id>, R, E>(
11
+ function makeDiskStoreInt<Id extends string, Encoded extends { id: Id }, R, E>(
12
12
  prefix: string,
13
13
  namespace: string,
14
14
  dir: string,
15
15
  name: string,
16
- seed?: Effect<Iterable<PM>, E, R>,
17
- defaultValues?: Partial<PM>
16
+ seed?: Effect<Iterable<Encoded>, E, R>,
17
+ defaultValues?: Partial<Encoded>
18
18
  ) {
19
+ type PM = PersistenceModelType<Encoded>
19
20
  return Effect.gen(function*($) {
20
21
  if (namespace !== "primary") {
21
22
  dir = dir + "/" + namespace
@@ -58,7 +59,7 @@ function makeDiskStoreInt<Id extends string, PM extends PersistenceModelType<Id>
58
59
  }
59
60
 
60
61
  const store = yield* $(
61
- makeMemoryStoreInt<Id, PM, R, E>(
62
+ makeMemoryStoreInt<Id, Encoded, R, E>(
62
63
  name,
63
64
  namespace,
64
65
  !fs.existsSync(file)
@@ -98,7 +99,7 @@ function makeDiskStoreInt<Id extends string, PM extends PersistenceModelType<Id>
98
99
  store.remove,
99
100
  Effect.tap(flushToDiskInBackground)
100
101
  )
101
- } satisfies Store<PM, Id>
102
+ } satisfies Store<Encoded, Id>
102
103
  })
103
104
  }
104
105
 
@@ -112,15 +113,15 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
112
113
  fs.mkdirSync(dir)
113
114
  }
114
115
  return {
115
- make: <Id extends string, PM extends PersistenceModelType<Id>, R, E>(
116
+ make: <Id extends string, Encoded extends { id: Id }, R, E>(
116
117
  name: string,
117
- seed?: Effect<Iterable<PM>, E, R>,
118
- config?: StoreConfig<PM>
118
+ seed?: Effect<Iterable<Encoded>, E, R>,
119
+ config?: StoreConfig<Encoded>
119
120
  ) =>
120
121
  Effect.gen(function*($) {
121
122
  const storesSem = Effect.unsafeMakeSemaphore(1)
122
123
  const primary = yield* $(makeDiskStoreInt(prefix, "primary", dir, name, seed, config?.defaultValues))
123
- const stores = new Map<string, Store<PM, Id>>([["primary", primary]])
124
+ const stores = new Map<string, Store<Encoded, Id>>([["primary", primary]])
124
125
  const ctx = yield* $(Effect.context<R>())
125
126
  const getStore = !config?.allowNamespace
126
127
  ? Effect.succeed(primary)
@@ -136,7 +137,7 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
136
137
  Effect.suspend(() => {
137
138
  const existing = stores.get(namespace)
138
139
  if (existing) return Effect.sync(() => existing)
139
- return makeDiskStoreInt<Id, PM, R, E>(prefix, namespace, dir, name, seed, config?.defaultValues)
140
+ return makeDiskStoreInt<Id, Encoded, R, E>(prefix, namespace, dir, name, seed, config?.defaultValues)
140
141
  .pipe(
141
142
  Effect.orDie,
142
143
  Effect.provide(ctx),
@@ -146,7 +147,7 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
146
147
  )
147
148
  }))
148
149
 
149
- const s: Store<PM, Id> = {
150
+ const s: Store<Encoded, Id> = {
150
151
  all: Effect.flatMap(getStore, (_) => _.all),
151
152
  find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
152
153
  filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
@@ -8,7 +8,7 @@ import type { FilterArgs, PersistenceModelType, Store, StoreConfig } from "./ser
8
8
  import { StoreMaker } from "./service.js"
9
9
  import { codeFilter, makeUpdateETag } from "./utils.js"
10
10
 
11
- export function memFilter<T extends PersistenceModelType<string>, U extends keyof T = never>(f: FilterArgs<T, U>) {
11
+ export function memFilter<T extends { id: string }, U extends keyof T = never>(f: FilterArgs<T, U>) {
12
12
  type M = U extends undefined ? T : Pick<T, U>
13
13
  return ((c: T[]): M[] => {
14
14
  const select = (r: T[]): M[] => (f.select ? r.map((_) => pick(_, f.select!)) : r) as any
@@ -78,18 +78,19 @@ function logQuery(f: FilterArgs<any, any>, defaultValues?: any) {
78
78
  }))
79
79
  }
80
80
 
81
- export function makeMemoryStoreInt<Id extends string, PM extends PersistenceModelType<Id>, R = never, E = never>(
81
+ export function makeMemoryStoreInt<Id extends string, Encoded extends { id: Id }, R = never, E = never>(
82
82
  modelName: string,
83
83
  namespace: string,
84
- seed?: Effect<Iterable<PM>, E, R>,
85
- _defaultValues?: Partial<PM>
84
+ seed?: Effect<Iterable<Encoded>, E, R>,
85
+ _defaultValues?: Partial<Encoded>
86
86
  ) {
87
+ type PM = PersistenceModelType<Encoded>
87
88
  return Effect.gen(function*($) {
88
89
  const updateETag = makeUpdateETag(modelName)
89
90
  const items_ = yield* $(seed ?? Effect.sync(() => []))
90
91
  const defaultValues = _defaultValues ?? {}
91
92
 
92
- const items = new Map([...items_].map((_) => [_.id, { ...defaultValues, ..._ }] as const))
93
+ const items = new Map([...items_].map((_) => [_.id, { _etag: undefined, ...defaultValues, ..._ }] as const))
93
94
  const store = Ref.unsafeMake<ReadonlyMap<Id, PM>>(items)
94
95
  const sem = Effect.unsafeMakeSemaphore(1)
95
96
  const withPermit = sem.withPermits(1)
@@ -120,7 +121,7 @@ export function makeMemoryStoreInt<Id extends string, PM extends PersistenceMode
120
121
  .map((_) => _ as NonEmptyArray<PM>),
121
122
  withPermit
122
123
  )
123
- const s: Store<PM, Id> = {
124
+ const s: Store<Encoded, Id> = {
124
125
  all: all.pipe(Effect.withSpan("Memory.all [effect-app/infra/Store]", {
125
126
  attributes: {
126
127
  modelName,
@@ -189,7 +190,7 @@ export function makeMemoryStoreInt<Id extends string, PM extends PersistenceMode
189
190
  attributes: { "repository.model_name": modelName, "repository.namespace": namespace }
190
191
  }))
191
192
  ),
192
- remove: (e: PM) =>
193
+ remove: (e: Encoded) =>
193
194
  Ref
194
195
  .get(store)
195
196
  .pipe(
@@ -206,14 +207,14 @@ export function makeMemoryStoreInt<Id extends string, PM extends PersistenceMode
206
207
  }
207
208
 
208
209
  export const makeMemoryStore = () => ({
209
- make: <Id extends string, PM extends PersistenceModelType<Id>, R = never, E = never>(
210
+ make: <Id extends string, Encoded extends { id: Id }, R = never, E = never>(
210
211
  modelName: string,
211
- seed?: Effect<Iterable<PM>, E, R>,
212
- config?: StoreConfig<PM>
212
+ seed?: Effect<Iterable<Encoded>, E, R>,
213
+ config?: StoreConfig<Encoded>
213
214
  ) =>
214
215
  Effect.gen(function*($) {
215
216
  const storesSem = Effect.unsafeMakeSemaphore(1)
216
- const primary = yield* $(makeMemoryStoreInt<Id, PM, R, E>(modelName, "primary", seed, config?.defaultValues))
217
+ const primary = yield* $(makeMemoryStoreInt<Id, Encoded, R, E>(modelName, "primary", seed, config?.defaultValues))
217
218
  const ctx = yield* $(Effect.context<R>())
218
219
  const stores = new Map([["primary", primary]])
219
220
  const getStore = !config?.allowNamespace
@@ -239,7 +240,7 @@ export const makeMemoryStore = () => ({
239
240
  )
240
241
  }))
241
242
  }))
242
- const s: Store<PM, Id> = {
243
+ const s: Store<Encoded, Id> = {
243
244
  all: Effect.flatMap(getStore, (_) => _.all),
244
245
  find: (...args) => Effect.flatMap(getStore, (_) => _.find(...args)),
245
246
  filter: (...args) => Effect.flatMap(getStore, (_) => _.filter(...args)),
@@ -14,7 +14,7 @@ describe("Optimistic Concurrency", () => {
14
14
  Effect.gen(function*($) {
15
15
  const existing = new Map<
16
16
  string,
17
- { _etag: string | undefined; id: string; a: string }
17
+ { _etag?: string | undefined; id: string; a: string }
18
18
  >()
19
19
  const { make } = makeMemoryStore()
20
20
  const store = yield* $(make("test", Effect.sync(() => existing)))
@@ -48,7 +48,7 @@ describe("Optimistic Concurrency", () => {
48
48
  Effect.gen(function*($) {
49
49
  const existing = new Map<
50
50
  string,
51
- { _etag: string | undefined; id: string; a: string }
51
+ { _etag?: string | undefined; id: string; a: string }
52
52
  >()
53
53
  const { make } = makeMemoryStore()
54
54
  const store = yield* $(make("test", Effect.sync(() => existing)))
@@ -86,7 +86,7 @@ describe("Optimistic Concurrency", () => {
86
86
  Effect.gen(function*($) {
87
87
  const existing = new Map<
88
88
  string,
89
- { _etag: string | undefined; id: string; a: string }
89
+ { _etag?: string | undefined; id: string; a: string }
90
90
  >()
91
91
  const { make } = makeMemoryStore()
92
92
  const store = yield* $(make("test", Effect.sync(() => existing)))
@@ -36,26 +36,30 @@ export type Where =
36
36
 
37
37
  export type Filter<E extends FieldValues> = QueryBuilder<E>
38
38
 
39
- export interface O<PM extends PersistenceModelType<unknown>> {
40
- key: keyof PM
39
+ export interface O<Encoded extends { id: string }> {
40
+ key: keyof Encoded
41
41
  direction: "ASC" | "DESC"
42
42
  }
43
43
 
44
- export interface FilterArgs<PM extends PersistenceModelType<unknown>, U extends keyof PM = never> {
45
- filter?: Filter<PM> | undefined
44
+ export interface FilterArgs<Encoded extends { id: string }, U extends keyof Encoded = never> {
45
+ filter?: Filter<Encoded> | undefined
46
46
  select?: NonEmptyReadonlyArray<U> | undefined
47
- order?: NonEmptyReadonlyArray<O<PM>>
47
+ order?: NonEmptyReadonlyArray<O<Encoded>>
48
48
  limit?: number | undefined
49
49
  skip?: number | undefined
50
50
  }
51
51
 
52
- export type FilterFunc<PM extends PersistenceModelType<unknown>> = <U extends keyof PM = never>(
53
- args: FilterArgs<PM, U>
54
- ) => Effect<(U extends undefined ? PM : Pick<PM, U>)[]>
52
+ export type FilterFunc<Encoded extends { id: string }> = <U extends keyof Encoded = never>(
53
+ args: FilterArgs<Encoded, U>
54
+ ) => Effect<(U extends undefined ? Encoded : Pick<Encoded, U>)[]>
55
55
 
56
- export interface Store<PM extends PersistenceModelType<Id>, Id> {
56
+ export interface Store<
57
+ Encoded extends { id: Id },
58
+ Id extends string,
59
+ PM extends PersistenceModelType<Encoded> = PersistenceModelType<Encoded>
60
+ > {
57
61
  all: Effect<PM[]>
58
- filter: FilterFunc<PM>
62
+ filter: FilterFunc<Encoded>
59
63
  find: (id: Id) => Effect<Option<PM>>
60
64
  set: (e: PM) => Effect<PM, OptimisticConcurrencyException>
61
65
  batchSet: (
@@ -65,9 +69,9 @@ export interface Store<PM extends PersistenceModelType<Id>, Id> {
65
69
  items: NonEmptyReadonlyArray<PM>
66
70
  ) => Effect<NonEmptyReadonlyArray<PM>, OptimisticConcurrencyException>
67
71
  /**
68
- * Requires the PM type, not Id, because various stores may need to calculate e.g partition keys.
72
+ * Requires the Encoded type, not Id, because various stores may need to calculate e.g partition keys.
69
73
  */
70
- remove: (e: PM) => Effect<void>
74
+ remove: (e: Encoded) => Effect<void>
71
75
  }
72
76
 
73
77
  /**
@@ -75,11 +79,11 @@ export interface Store<PM extends PersistenceModelType<Id>, Id> {
75
79
  * @tsplus companion StoreMaker.Ops
76
80
  */
77
81
  export class StoreMaker extends TagClassId("effect-app/StoreMaker")<StoreMaker, {
78
- make: <PM extends PersistenceModelType<Id>, Id extends string, R = never, E = never>(
82
+ make: <Encoded extends { id: Id }, Id extends string, R = never, E = never>(
79
83
  name: string,
80
- seed?: Effect<Iterable<PM>, E, R>,
81
- config?: StoreConfig<PM>
82
- ) => Effect<Store<PM, Id>, E, R>
84
+ seed?: Effect<Iterable<Encoded>, E, R>,
85
+ config?: StoreConfig<Encoded>
86
+ ) => Effect<Store<Encoded, Id>, E, R>
83
87
  }>() {
84
88
  }
85
89
 
@@ -157,9 +161,8 @@ const makeMap = Effect.sync(() => makeContextMap())
157
161
  export class ContextMap extends TagClassMakeId("effect-app/ContextMap", makeMap)<ContextMap>() {
158
162
  }
159
163
 
160
- export interface PersistenceModelType<Id> extends Record<string, any> {
161
- id: Id
162
- _etag: string | undefined
164
+ export type PersistenceModelType<Encoded> = Encoded & {
165
+ _etag?: string | undefined
163
166
  }
164
167
 
165
168
  export interface StorageConfig {
@@ -4,14 +4,14 @@ import { OptimisticConcurrencyException } from "../../errors.js"
4
4
  import { codeFilter3_ } from "./codeFilter.js"
5
5
  import type { Filter, PersistenceModelType, SupportedValues2 } from "./service.js"
6
6
 
7
- export const makeETag = <E extends PersistenceModelType<Id>, Id extends string>(
7
+ export const makeETag = <E extends PersistenceModelType<{}>>(
8
8
  { _etag, ...e }: E
9
9
  ): E => (({
10
10
  ...e,
11
11
  _etag: objectHash(e)
12
12
  }) as any)
13
13
  export const makeUpdateETag =
14
- (type: string) => <E extends PersistenceModelType<Id>, Id extends string>(e: E, current: Option<E>) =>
14
+ (type: string) => <E extends PersistenceModelType<{ id: string }>>(e: E, current: Option<E>) =>
15
15
  Effect.gen(function*($) {
16
16
  if (e._etag) {
17
17
  yield* $(
@@ -67,7 +67,7 @@ it("works", () => {
67
67
  expect(processed).toEqual(items.slice(0, 2).toReversed().map((_) => pick(_, "id", "displayName")))
68
68
  })
69
69
 
70
- class TestRepo extends RepositoryDefaultImpl<TestRepo>()<s.From & { _etag: string | undefined }, never>()(
70
+ class TestRepo extends RepositoryDefaultImpl<TestRepo>()(
71
71
  "test",
72
72
  s
73
73
  ) {