@effect-app/infra 0.43.1 → 0.44.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 (47) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/_cjs/api/codec.cjs +3 -11
  3. package/_cjs/api/codec.cjs.map +1 -1
  4. package/_cjs/services/RepositoryBase.cjs +201 -0
  5. package/_cjs/services/RepositoryBase.cjs.map +1 -0
  6. package/_cjs/services/Store/Cosmos.cjs +1 -1
  7. package/_cjs/services/Store/Cosmos.cjs.map +1 -1
  8. package/_cjs/services/Store/Disk.cjs +1 -1
  9. package/_cjs/services/Store/Disk.cjs.map +1 -1
  10. package/_cjs/services/Store/Memory.cjs +2 -1
  11. package/_cjs/services/Store/Memory.cjs.map +1 -1
  12. package/_cjs/services/Store/Redis.cjs +2 -2
  13. package/_cjs/services/Store/Redis.cjs.map +1 -1
  14. package/_src/api/codec.ts +3 -15
  15. package/_src/services/RepositoryBase.ts +366 -0
  16. package/_src/services/Store/Cosmos.ts +3 -3
  17. package/_src/services/Store/Disk.ts +8 -8
  18. package/_src/services/Store/Memory.ts +7 -6
  19. package/_src/services/Store/Redis.ts +4 -4
  20. package/_src/services/Store/service.ts +4 -4
  21. package/dist/api/codec.d.ts +2 -3
  22. package/dist/api/codec.d.ts.map +1 -1
  23. package/dist/api/codec.js +4 -12
  24. package/dist/api/express/makeOpenApiSpecs.d.ts +1 -1
  25. package/dist/services/Repository.d.ts +14 -14
  26. package/dist/services/RepositoryBase.d.ts +240 -0
  27. package/dist/services/RepositoryBase.d.ts.map +1 -0
  28. package/dist/services/RepositoryBase.js +175 -0
  29. package/dist/services/Store/Cosmos.d.ts +1 -1
  30. package/dist/services/Store/Cosmos.d.ts.map +1 -1
  31. package/dist/services/Store/Cosmos.js +2 -2
  32. package/dist/services/Store/Disk.d.ts +1 -1
  33. package/dist/services/Store/Disk.d.ts.map +1 -1
  34. package/dist/services/Store/Disk.js +3 -3
  35. package/dist/services/Store/Memory.d.ts +2 -2
  36. package/dist/services/Store/Memory.d.ts.map +1 -1
  37. package/dist/services/Store/Memory.js +3 -2
  38. package/dist/services/Store/Redis.d.ts +1 -1
  39. package/dist/services/Store/Redis.d.ts.map +1 -1
  40. package/dist/services/Store/Redis.js +3 -3
  41. package/dist/services/Store/service.d.ts +1 -1
  42. package/dist/services/Store/service.d.ts.map +1 -1
  43. package/dist/services/simpledb/cosmosdb.d.ts +2 -2
  44. package/dist/services/simpledb/diskdb.d.ts +1 -1
  45. package/dist/services/simpledb/redisdb.d.ts +1 -1
  46. package/dist/services/simpledb/simpledb.d.ts +2 -2
  47. package/package.json +13 -3
@@ -0,0 +1,366 @@
1
+ import type { ParserEnv } from "@effect-app/schema/custom/Parser"
2
+ import type { Repository } from "./Repository.js"
3
+ import type { RequestContextContainer } from "./RequestContextContainer.js"
4
+ import { ContextMap, StoreMaker } from "./Store.js"
5
+ import type { Filter, StoreConfig, Where } from "./Store.js"
6
+ import type {} from "@effect/data/Equal"
7
+ import type {} from "@effect/data/Hash"
8
+ import type { Opt } from "@effect-app/core/Option"
9
+ import { makeCodec } from "@effect-app/infra/api/codec"
10
+ import { makeFilters } from "@effect-app/infra/filter"
11
+ import type { Chunk, Schema } from "@effect-app/prelude"
12
+ import { EParserFor } from "@effect-app/prelude/schema"
13
+ import type { InvalidStateError, OptimisticConcurrencyException } from "../errors.js"
14
+
15
+ /**
16
+ * A base for creating an abstract class usable as Tag, Companion and interface to create your own implementation.
17
+ */
18
+ export const RepositoryBase = <Service>() => {
19
+ return <T extends { id: string }, PM extends { id: string; _etag: string | undefined }, Evt, ItemType extends string>(
20
+ itemType: ItemType
21
+ ) => {
22
+ abstract class RepositoryBaseC implements Repository<T, PM, Evt, ItemType> {
23
+ itemType: ItemType = itemType
24
+ abstract find: (id: T["id"]) => Effect<ContextMap | RequestContextContainer, never, Opt<T>>
25
+ abstract all: Effect<ContextMap, never, Chunk<T>>
26
+ abstract saveAndPublish: (
27
+ items: Iterable<T>,
28
+ events?: Iterable<Evt>
29
+ ) => Effect<ContextMap | RequestContextContainer, InvalidStateError | OptimisticConcurrencyException, void>
30
+ abstract utils: {
31
+ mapReverse: (
32
+ pm: PM,
33
+ setEtag: (id: string, eTag: string | undefined) => void
34
+ ) => unknown // TODO
35
+ parse: (a: unknown, env?: ParserEnv | undefined) => T
36
+ all: Effect<ContextMap, never, Chunk<PM>>
37
+ filter: (filter: Filter<PM>, cursor?: { limit?: number; skip?: number }) => Effect<ContextMap, never, Chunk<PM>>
38
+ }
39
+ abstract remove: (item: T) => Effect<ContextMap, never, void>
40
+ static where = makeWhere<PM>()
41
+ static flatMap<R1, E1, B>(f: (a: Service) => Effect<R1, E1, B>): Effect<Service | R1, E1, B> {
42
+ return Effect.flatMap(this as unknown as Tag<Service, Service>, f)
43
+ }
44
+ static map<B>(f: (a: Service) => B): Effect<Service, never, B> {
45
+ return Effect.map(this as unknown as Tag<Service, Service>, f)
46
+ }
47
+ }
48
+ return assignTag<Service>()(RepositoryBaseC)
49
+ }
50
+ }
51
+
52
+ /**
53
+ * A base implementation to create a repository.
54
+ */
55
+ export function makeRepo<
56
+ PM extends { id: string; _etag: string | undefined },
57
+ Evt = never
58
+ >() {
59
+ return <
60
+ ItemType extends string,
61
+ T extends { id: string },
62
+ ConstructorInput,
63
+ Api,
64
+ E extends { id: string }
65
+ >(
66
+ name: ItemType,
67
+ schema: Schema.Schema<unknown, T, ConstructorInput, E, Api>,
68
+ mapFrom: (pm: Omit<PM, "_etag">) => E,
69
+ mapTo: (e: E, etag: string | undefined) => PM
70
+ ) => {
71
+ const where = makeWhere<PM>()
72
+
73
+ function mapToPersistenceModel(
74
+ e: E,
75
+ getEtag: (id: string) => string | undefined
76
+ ): PM {
77
+ return mapTo(e, getEtag(e.id))
78
+ }
79
+
80
+ function mapReverse(
81
+ { _etag, ...e }: PM,
82
+ setEtag: (id: string, eTag: string | undefined) => void
83
+ ): E {
84
+ setEtag(e.id, _etag)
85
+ return mapFrom(e)
86
+ }
87
+
88
+ const mkStore = makeStore<PM>()(name, schema, mapTo)
89
+
90
+ function make(
91
+ publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<RequestContextContainer, never, void>,
92
+ makeInitial?: Effect<never, never, readonly T[]>,
93
+ config?: Omit<StoreConfig<PM>, "partitionValue"> & {
94
+ partitionValue?: (a: PM) => string
95
+ }
96
+ ) {
97
+ return Do($ => {
98
+ const store = $(mkStore(makeInitial, config))
99
+
100
+ const allE = store.all.flatMap(items =>
101
+ Do($ => {
102
+ const { set } = $(ContextMap)
103
+ return items.map(_ => mapReverse(_, set))
104
+ })
105
+ )
106
+
107
+ const all = allE.flatMap(_ => _.forEachEffect(EParserFor(schema).condemnDie))
108
+
109
+ function findE(id: T["id"]) {
110
+ return store.find(id)
111
+ .flatMap(items =>
112
+ Do($ => {
113
+ const { set } = $(ContextMap)
114
+ return items.map(_ => mapReverse(_, set))
115
+ })
116
+ )
117
+ }
118
+
119
+ function find(id: T["id"]) {
120
+ return findE(id).flatMapOpt(EParserFor(schema).condemnDie)
121
+ }
122
+
123
+ const saveAllE = (a: Iterable<E>) =>
124
+ Effect(a.toNonEmptyArray)
125
+ .flatMapOpt(a =>
126
+ Do($ => {
127
+ const { get, set } = $(ContextMap)
128
+ const items = a.mapNonEmpty(_ => mapToPersistenceModel(_, get))
129
+ const ret = $(store.batchSet(items))
130
+ ret.forEach(_ => set(_.id, _._etag))
131
+ })
132
+ )
133
+
134
+ const saveAll = (a: Iterable<T>) => saveAllE(a.toChunk.map(Encoder.for(schema)))
135
+
136
+ const saveAndPublish = (items: Iterable<T>, events: Iterable<Evt> = []) =>
137
+ saveAll(items)
138
+ > Effect(events.toNonEmptyArray)
139
+ // TODO: for full consistency the events should be stored within the same database transaction, and then picked up.
140
+ .flatMapOpt(publishEvents)
141
+
142
+ const encode = Encoder.for(schema)
143
+ function remove(item: T) {
144
+ return Do($ => {
145
+ const { get, set } = $(ContextMap)
146
+ const e = encode(item)
147
+ $(store.remove(mapToPersistenceModel(e, get)))
148
+ set(item.id, undefined)
149
+ })
150
+ }
151
+
152
+ const r: Repository<T, PM, Evt, ItemType> = {
153
+ /**
154
+ * @internal
155
+ */
156
+ utils: {
157
+ mapReverse,
158
+ parse: Parser.for(schema).unsafe,
159
+ filter: store.filter
160
+ .flow(_ => _.tap(items => ContextMap.map(({ set }) => items.forEach(_ => set(_.id, _._etag))))),
161
+ all: store.all.tap(items => ContextMap.map(({ set }) => items.forEach(_ => set(_.id, _._etag))))
162
+ },
163
+ itemType: name,
164
+ find,
165
+ all,
166
+ saveAndPublish,
167
+ remove
168
+ }
169
+ return r
170
+ })
171
+ }
172
+
173
+ return {
174
+ make,
175
+ where
176
+ }
177
+ }
178
+ }
179
+
180
+ /**
181
+ * @tsplus fluent Repository removeById
182
+ */
183
+ export function removeById<
184
+ T extends { id: string },
185
+ PM extends { id: string },
186
+ Evt,
187
+ ItemType extends string
188
+ >(
189
+ self: Repository<T, PM, Evt, ItemType>,
190
+ id: T["id"]
191
+ ) {
192
+ return self.get(id).flatMap(_ => self.remove(_))
193
+ }
194
+
195
+ export function makeWhere<PM extends { id: string; _etag: string | undefined }>() {
196
+ const f_ = makeFilters<PM>()
197
+ type WhereFilter = typeof f_
198
+
199
+ function makeFilter_(filter: (f: WhereFilter) => Filter<PM>) {
200
+ return filter(f_)
201
+ }
202
+
203
+ function where(
204
+ makeWhere: (
205
+ f: WhereFilter
206
+ ) => Where | readonly [Where, ...Where[]],
207
+ mode?: "or" | "and"
208
+ ) {
209
+ return makeFilter_(f => {
210
+ const m = makeWhere ? makeWhere(f) : []
211
+ return ({
212
+ mode,
213
+ where: (Array.isArray(m) ? m as unknown as [Where, ...Where[]] : [m]) as readonly [Where, ...Where[]]
214
+ })
215
+ })
216
+ }
217
+ return where
218
+ }
219
+
220
+ const pluralize = (s: string) =>
221
+ s.endsWith("s")
222
+ ? s + "es"
223
+ : s.endsWith("y")
224
+ ? s.substring(0, s.length - 1) + "ies"
225
+ : s + "s"
226
+
227
+ export function makeStore<
228
+ PM extends { id: string; _etag: string | undefined }
229
+ >() {
230
+ return <
231
+ ItemType extends string,
232
+ T extends { id: string },
233
+ ConstructorInput,
234
+ Api,
235
+ E extends { id: string }
236
+ >(
237
+ name: ItemType,
238
+ schema: Schema.Schema<unknown, T, ConstructorInput, E, Api>,
239
+ mapTo: (e: E, etag: string | undefined) => PM
240
+ ) => {
241
+ const [_dec, encode] = makeCodec(schema)
242
+ function encodeToPM() {
243
+ const getEtag = () => undefined
244
+ return flow(encode, v => mapToPersistenceModel(v, getEtag))
245
+ }
246
+
247
+ function mapToPersistenceModel(
248
+ e: E,
249
+ getEtag: (id: string) => string | undefined
250
+ ): PM {
251
+ return mapTo(e, getEtag(e.id))
252
+ }
253
+
254
+ function makeStore(
255
+ makeInitial?: Effect<never, never, readonly T[]>,
256
+ config?: Omit<StoreConfig<PM>, "partitionValue"> & {
257
+ partitionValue?: (a: PM) => string
258
+ }
259
+ ) {
260
+ return Do($ => {
261
+ const { make } = $(StoreMaker)
262
+
263
+ const store = $(
264
+ make<PM, string>(
265
+ pluralize(name),
266
+ makeInitial
267
+ ? makeInitial
268
+ .map(_ => _.map(encodeToPM()))
269
+ : undefined,
270
+ {
271
+ ...config,
272
+ partitionValue: config?.partitionValue ?? (_ => "primary") /*(isIntegrationEvent(r) ? r.companyId : r.id*/
273
+ }
274
+ )
275
+ )
276
+ return store
277
+ })
278
+ }
279
+
280
+ return makeStore
281
+ }
282
+ }
283
+
284
+ export const RepositoryBaseImpl = <Service>() => {
285
+ return <
286
+ PM extends { id: string; _etag: string | undefined },
287
+ Evt = never
288
+ >() =>
289
+ <ItemType extends string, T extends { id: string }, ConstructorInput, Api, E extends { id: string }>(
290
+ itemType: ItemType,
291
+ schema: Schema.Schema<unknown, T, ConstructorInput, E, Api>,
292
+ mapFrom: (pm: Omit<PM, "_etag">) => E,
293
+ mapTo: (e: E, etag: string | undefined) => PM
294
+ ): (abstract new() => Repository<T, PM, Evt, ItemType>) & Tag<Service, Service> & {
295
+ make(
296
+ publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<RequestContextContainer, never, void>,
297
+ makeInitial?: Effect<never, never, readonly T[]>,
298
+ config?: Omit<StoreConfig<PM>, "partitionValue"> & {
299
+ partitionValue?: (a: PM) => string
300
+ }
301
+ ): Effect<StoreMaker, never, Repository<T, PM, Evt, ItemType>>
302
+ where: ReturnType<typeof makeWhere<PM>>
303
+ flatMap: <R1, E1, B>(f: (a: Service) => Effect<R1, E1, B>) => Effect<Service | R1, E1, B>
304
+ map: <B>(f: (a: Service) => B) => Effect<Service, never, B>
305
+ } => {
306
+ const mkRepo = makeRepo<PM, Evt>()(itemType, schema, mapFrom, mapTo)
307
+ abstract class Cls extends RepositoryBase<Service>()<T, PM, Evt, ItemType>(itemType) {
308
+ static readonly make = mkRepo.make
309
+ }
310
+ return Cls
311
+ }
312
+ }
313
+
314
+ export const RepositoryDefaultImpl = <Service>() => {
315
+ return <
316
+ PM extends { id: string; _etag: string | undefined },
317
+ Evt = never
318
+ >() =>
319
+ <ItemType extends string, T extends { id: string }, ConstructorInput, Api, E extends { id: string }>(
320
+ itemType: ItemType,
321
+ schema: Schema.Schema<unknown, T, ConstructorInput, E, Api>,
322
+ mapFrom: (pm: Omit<PM, "_etag">) => E,
323
+ mapTo: (e: E, etag: string | undefined) => PM
324
+ ): Tag<Service, Service> & {
325
+ new(
326
+ impl: Repository<T, PM, Evt, ItemType>
327
+ ): Repository<T, PM, Evt, ItemType>
328
+ make(
329
+ publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<RequestContextContainer, never, void>,
330
+ makeInitial?: Effect<never, never, readonly T[]>,
331
+ config?: Omit<StoreConfig<PM>, "partitionValue"> & {
332
+ partitionValue?: (a: PM) => string
333
+ }
334
+ ): Effect<StoreMaker, never, Repository<T, PM, Evt, ItemType>>
335
+ toLayer(
336
+ publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<RequestContextContainer, never, void>,
337
+ makeInitial?: Effect<never, never, readonly T[]>,
338
+ config?: Omit<StoreConfig<PM>, "partitionValue"> & {
339
+ partitionValue?: (a: PM) => string
340
+ }
341
+ ): Layer<StoreMaker, never, Service>
342
+ where: ReturnType<typeof makeWhere<PM>>
343
+ flatMap: <R1, E1, B>(f: (a: Service) => Effect<R1, E1, B>) => Effect<Service | R1, E1, B>
344
+ map: <B>(f: (a: Service) => B) => Effect<Service, never, B>
345
+ repo: Repository<T, PM, Evt, ItemType> // just a helper to type the constructor
346
+ } => {
347
+ return class extends RepositoryBaseImpl<Service>()<PM, Evt>()(itemType, schema, mapFrom, mapTo) {
348
+ static toLayer(
349
+ publishEvents: (evt: NonEmptyReadonlyArray<Evt>) => Effect<RequestContextContainer, never, void>,
350
+ makeInitial?: Effect<never, never, readonly T[]>,
351
+ config?: Omit<StoreConfig<PM>, "partitionValue"> & {
352
+ partitionValue?: (a: PM) => string
353
+ }
354
+ ) {
355
+ return this.make(publishEvents, makeInitial, config).map(impl => new this(impl) as any as Service).toLayer(this)
356
+ }
357
+ static repo: any
358
+ constructor(
359
+ impl: Repository<T, PM, Evt, ItemType>
360
+ ) {
361
+ super()
362
+ Object.assign(this, impl)
363
+ }
364
+ }
365
+ }
366
+ }
@@ -25,9 +25,9 @@ export function makeCosmosStore({ prefix }: StorageConfig) {
25
25
  return Effect.gen(function*($) {
26
26
  const { db } = yield* $(CosmosClient.CosmosClient)
27
27
  return {
28
- make: <Id extends string, PM extends PersistenceModelType<Id>, Id2 extends Id>(
28
+ make: <Id extends string, PM extends PersistenceModelType<Id>>(
29
29
  name: string,
30
- seed?: Effect<never, never, ReadonlyMap<Id2, PM>>,
30
+ seed?: Effect<never, never, Iterable<PM>>,
31
31
  config?: StoreConfig<PM>
32
32
  ) =>
33
33
  Effect.gen(function*($) {
@@ -337,7 +337,7 @@ export function makeCosmosStore({ prefix }: StorageConfig) {
337
337
  if (seed) {
338
338
  const m = yield* $(seed)
339
339
  yield* $(
340
- Effect([...m.values()].toNonEmpty)
340
+ Effect(m.toNonEmptyArray)
341
341
  .flatMapOpt(a =>
342
342
  s
343
343
  .bulkSet(a)
@@ -7,12 +7,12 @@ import { makeMemoryStoreInt, storeId } from "./Memory.js"
7
7
  import type { PersistenceModelType, StorageConfig, Store, StoreConfig } from "./service.js"
8
8
  import { StoreMaker } from "./service.js"
9
9
 
10
- function makeDiskStoreInt<Id extends string, Id2 extends Id, PM extends PersistenceModelType<Id>>(
10
+ function makeDiskStoreInt<Id extends string, PM extends PersistenceModelType<Id>>(
11
11
  prefix: string,
12
12
  namespace: string,
13
13
  dir: string,
14
14
  name: string,
15
- seed?: Effect<never, never, ReadonlyMap<Id2, PM>>
15
+ seed?: Effect<never, never, Iterable<PM>>
16
16
  ) {
17
17
  return Effect.gen(function*($) {
18
18
  const file = dir + "/" + prefix + name + (namespace === "primary" ? "" : "-" + namespace) + ".json"
@@ -25,11 +25,11 @@ function makeDiskStoreInt<Id extends string, Id2 extends Id, PM extends Persiste
25
25
  }
26
26
 
27
27
  const store = yield* $(
28
- makeMemoryStoreInt<Id, Id, PM>(
28
+ makeMemoryStoreInt<Id, PM>(
29
29
  name,
30
30
  !fs.existsSync(file)
31
- ? seed ?? Effect(new Map())
32
- : fsStore.get.map(x => new Map(x.map(x => [x.id, x] as const)))
31
+ ? seed
32
+ : fsStore.get
33
33
  )
34
34
  )
35
35
 
@@ -70,9 +70,9 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
70
70
  fs.mkdirSync(dir)
71
71
  }
72
72
  return {
73
- make: <Id extends string, Id2 extends Id, PM extends PersistenceModelType<Id>>(
73
+ make: <Id extends string, PM extends PersistenceModelType<Id>>(
74
74
  name: string,
75
- seed?: Effect<never, never, ReadonlyMap<Id2, PM>>,
75
+ seed?: Effect<never, never, Iterable<PM>>,
76
76
  config?: StoreConfig<PM>
77
77
  ) =>
78
78
  Effect.gen(function*($) {
@@ -91,7 +91,7 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
91
91
  Effect.suspend(() => {
92
92
  const existing = stores.get(namespace)
93
93
  if (existing) return Effect(existing)
94
- return makeDiskStoreInt<Id, Id2, PM>(prefix, namespace, dir, name, seed).tap(store =>
94
+ return makeDiskStoreInt<Id, PM>(prefix, namespace, dir, name, seed).tap(store =>
95
95
  Effect.sync(() => stores.set(namespace, store))
96
96
  )
97
97
  })
@@ -28,13 +28,14 @@ export const restoreFromRequestContext = RequestContextContainer.get.flatMap(ctx
28
28
  storeId.set(ctx.namespace ?? "primary")
29
29
  )
30
30
 
31
- export function makeMemoryStoreInt<Id extends string, Id2 extends Id, PM extends PersistenceModelType<Id>>(
31
+ export function makeMemoryStoreInt<Id extends string, PM extends PersistenceModelType<Id>>(
32
32
  name: string,
33
- seed?: Effect<never, never, ReadonlyMap<Id2, PM>>
33
+ seed?: Effect<never, never, Iterable<PM>>
34
34
  ) {
35
35
  return Effect.gen(function*($) {
36
36
  const updateETag = makeUpdateETag(name)
37
- const items: ReadonlyMap<Id, PM> = yield* $(seed ?? Effect(new Map<Id, PM>()))
37
+ const items_ = yield* $(seed ?? Effect([]))
38
+ const items = new Map(items_.toChunk.map(_ => [_.id, _] as const))
38
39
  const makeStore = (): ReadonlyMap<Id, PM> => new Map([...items.entries()].map(([id, e]) => [id, makeETag(e)]))
39
40
  const store = Ref.unsafeMake(makeStore())
40
41
  const sem = Semaphore.unsafeMake(1)
@@ -78,14 +79,14 @@ export function makeMemoryStoreInt<Id extends string, Id2 extends Id, PM extends
78
79
  }
79
80
 
80
81
  export const makeMemoryStore = () => ({
81
- make: <Id extends string, Id2 extends Id, PM extends PersistenceModelType<Id>>(
82
+ make: <Id extends string, PM extends PersistenceModelType<Id>>(
82
83
  name: string,
83
- seed?: Effect<never, never, ReadonlyMap<Id2, PM>>,
84
+ seed?: Effect<never, never, Iterable<PM>>,
84
85
  config?: StoreConfig<PM>
85
86
  ) =>
86
87
  Effect.gen(function*($) {
87
88
  const storesSem = Semaphore.unsafeMake(1)
88
- const primary = yield* $(makeMemoryStoreInt<Id, Id2, PM>(name, seed))
89
+ const primary = yield* $(makeMemoryStoreInt<Id, PM>(name, seed))
89
90
  const stores = new Map([["primary", primary]])
90
91
  const getStore = !config?.allowNamespace ? Effect.succeed(primary) : storeId.get.flatMap(namespace => {
91
92
  const store = stores.get(namespace)
@@ -11,9 +11,9 @@ export function makeRedisStore({ prefix }: StorageConfig) {
11
11
  return Effect.gen(function*($) {
12
12
  const redis = yield* $(RedisClient.RedisClient)
13
13
  return {
14
- make: <Id extends string, PM extends PersistenceModelType<Id>, Id2 extends Id>(
14
+ make: <Id extends string, PM extends PersistenceModelType<Id>>(
15
15
  name: string,
16
- seed?: Effect<never, never, ReadonlyMap<Id2, PM>>,
16
+ seed?: Effect<never, never, Iterable<PM>>,
17
17
  _config?: StoreConfig<PM>
18
18
  ) =>
19
19
  Effect.gen(function*($) {
@@ -22,9 +22,9 @@ export function makeRedisStore({ prefix }: StorageConfig) {
22
22
  const key = `${prefix}${name}`
23
23
  const current = yield* $(RedisClient.get(key).orDie.provideService(RedisClient.RedisClient, redis))
24
24
  if (!current.isSome()) {
25
- const m = yield* $(seed ?? Effect(new Map()))
25
+ const m = yield* $(seed ?? Effect([]))
26
26
  yield* $(
27
- RedisClient.set(key, JSON.stringify({ data: [...m.values()].map(e => makeETag(e)) }))
27
+ RedisClient.set(key, JSON.stringify({ data: [...m].map(e => makeETag(e)) }))
28
28
  .orDie
29
29
  .provideService(RedisClient.RedisClient, redis)
30
30
  )
@@ -84,11 +84,11 @@ export interface Store<PM extends PersistenceModelType<Id>, Id extends string> {
84
84
  * @tsplus companion StoreMaker.Ops
85
85
  */
86
86
  export abstract class StoreMaker extends TagClass<StoreMaker>() {
87
- abstract make: <E extends PersistenceModelType<Id>, Id extends string, Id2 extends Id>(
87
+ abstract make: <PM extends PersistenceModelType<Id>, Id extends string>(
88
88
  name: string,
89
- seed?: Effect<never, never, ReadonlyMap<Id2, E>>,
90
- config?: StoreConfig<E>
91
- ) => Effect<never, never, Store<E, Id>>
89
+ seed?: Effect<never, never, Iterable<PM>>,
90
+ config?: StoreConfig<PM>
91
+ ) => Effect<never, never, Store<PM, Id>>
92
92
  }
93
93
 
94
94
  /**
@@ -1,7 +1,6 @@
1
- import { Effect } from "@effect/io/Effect";
2
1
  import * as MO from "@effect-app/schema";
3
- import type { ParserEnv } from "@effect-app/schema/custom/Parser";
2
+ import { Encoder, Parser } from "@effect-app/schema";
4
3
  export declare function makeCodec<ParsedShape extends {
5
4
  id: Id;
6
- }, ConstructorInput, Encoded, Api, Id>(self: MO.Schema<unknown, ParsedShape, ConstructorInput, Encoded, Api>): readonly [(e: Encoded, env?: ParserEnv) => import("@effect/io/Effect").Effect<never, never, ParsedShape>, (u: ParsedShape) => import("@effect/io/Effect").Effect<never, never, Encoded>, (a: readonly ParsedShape[]) => import("@effect/io/Effect").Effect<never, never, Map<ParsedShape["id"], Encoded>>];
5
+ }, ConstructorInput, Encoded, Api, Id>(self: MO.Schema<unknown, ParsedShape, ConstructorInput, Encoded, Api>): readonly [(a: unknown, env?: Parser.ParserEnv | undefined) => import("@effect/io/Effect").Effect<never, never, ParsedShape>, Encoder.Encoder<ParsedShape, Encoded>];
7
6
  //# sourceMappingURL=codec.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../_src/api/codec.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAExC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAA;AAEjE,wBAAgB,SAAS,CACvB,WAAW,SAAS;IAAE,EAAE,EAAE,EAAE,CAAA;CAAE,EAC9B,gBAAgB,EAChB,OAAO,EACP,GAAG,EACH,EAAE,EACF,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,OAAO,EAAE,GAAG,CAAC,iBAGlD,OAAO,QAAQ,SAAS,wEAGxB,WAAW,kLAG/B"}
1
+ {"version":3,"file":"codec.d.ts","sourceRoot":"","sources":["../../_src/api/codec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAEpD,wBAAgB,SAAS,CACvB,WAAW,SAAS;IAAE,EAAE,EAAE,EAAE,CAAA;CAAE,EAC9B,gBAAgB,EAChB,OAAO,EACP,GAAG,EACH,EAAE,EACF,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,OAAO,EAAE,GAAG,CAAC,uKAKtE"}
package/dist/api/codec.js CHANGED
@@ -1,17 +1,9 @@
1
- import * as tsplus_module_1 from "@effect/io/Effect";
2
- import * as tsplus_module_2 from "@effect-app/core/_ext/Iterable";
3
1
  import * as MO from "@effect-app/schema";
4
2
  import { Encoder, Parser } from "@effect-app/schema";
5
3
  export function makeCodec(self) {
6
- const parse = MO.condemnDie(Parser.for(self));
4
+ const decode = MO.condemnDie(Parser.for(self));
7
5
  // TODO: strict
8
- const decode = (e, env) => parse(e, env);
9
- const enc = Encoder.for(self);
10
- const encode = (u) => tsplus_module_1.sync(() => enc(u));
11
- const encodeToMap = toMap(encode);
12
- return [decode, encode, encodeToMap];
6
+ const encode = Encoder.for(self);
7
+ return [decode, encode];
13
8
  }
14
- function toMap(encode) {
15
- return (a) => tsplus_module_1.map(tsplus_module_2.ext_collectAll(a.map(task => tsplus_module_1.all(tsplus_module_1.sync(() => task.id), encode(task)))), _ => new Map(_));
16
- }
17
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29kZWMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9fc3JjL2FwaS9jb2RlYy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUFBLE9BQU8sS0FBSyxFQUFFLE1BQU0sb0JBQW9CLENBQUE7QUFDeEMsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQTtBQUdwRCxNQUFNLFVBQVUsU0FBUyxDQU12QixJQUFxRTtJQUNyRSxNQUFNLEtBQUssR0FBRyxFQUFFLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQTtJQUM3QyxlQUFlO0lBQ2YsTUFBTSxNQUFNLEdBQUcsQ0FBQyxDQUFVLEVBQUUsR0FBZSxFQUFFLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFBO0lBQzdELE1BQU0sR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUE7SUFFN0IsTUFBTSxNQUFNLEdBQUcsQ0FBQyxDQUFjLEVBQUUsRUFBRSxDQUFDLDJCQUFPLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBO0lBQ2pELE1BQU0sV0FBVyxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUMsQ0FBQTtJQUNqQyxPQUFPLENBQUMsTUFBTSxFQUFFLE1BQU0sRUFBRSxXQUFXLENBQVUsQ0FBQTtBQUMvQyxDQUFDO0FBRUQsU0FBUyxLQUFLLENBQThCLE1BQXlDO0lBQ25GLE9BQU8sQ0FBQyxDQUFtQixFQUFFLEVBQUUsQ0FDN0Isb0JBQUEsK0JBQUEsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLG9CQUFXLDJCQUFPLElBQUksQ0FBQyxFQUFhLENBQUMsRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUNwRCxFQUNSLENBQUMsQ0FBQyxFQUFFLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQTtBQUMzQixDQUFDIn0=
9
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29kZWMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9fc3JjL2FwaS9jb2RlYy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssRUFBRSxNQUFNLG9CQUFvQixDQUFBO0FBQ3hDLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sb0JBQW9CLENBQUE7QUFFcEQsTUFBTSxVQUFVLFNBQVMsQ0FNdkIsSUFBcUU7SUFDckUsTUFBTSxNQUFNLEdBQUcsRUFBRSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUE7SUFDOUMsZUFBZTtJQUNmLE1BQU0sTUFBTSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUE7SUFDaEMsT0FBTyxDQUFDLE1BQU0sRUFBRSxNQUFNLENBQVUsQ0FBQTtBQUNsQyxDQUFDIn0=
@@ -18,7 +18,7 @@ export declare function makeOpenApiSpecs(rdescs: Iterable<RouteDescriptorAny>, i
18
18
  version: string;
19
19
  };
20
20
  tags: never[];
21
- paths: Record<string, Record<"PATCH" | "GET" | "POST" | "PUT" | "DELETE", {
21
+ paths: Record<string, Record<"PATCH" | "GET" | "PUT" | "POST" | "DELETE", {
22
22
  [x: string]: {
23
23
  responses: any;
24
24
  tags: readonly string[] | undefined;