@effect-app/infra 4.0.0-beta.8 → 4.0.0-beta.82

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 (177) hide show
  1. package/CHANGELOG.md +540 -0
  2. package/dist/CUPS.d.ts +3 -3
  3. package/dist/CUPS.d.ts.map +1 -1
  4. package/dist/CUPS.js +3 -3
  5. package/dist/Emailer/Sendgrid.js +1 -1
  6. package/dist/Emailer/service.d.ts +3 -3
  7. package/dist/Emailer/service.d.ts.map +1 -1
  8. package/dist/Emailer/service.js +3 -3
  9. package/dist/MainFiberSet.d.ts +2 -2
  10. package/dist/MainFiberSet.d.ts.map +1 -1
  11. package/dist/MainFiberSet.js +3 -3
  12. package/dist/Model/Repository/internal/internal.d.ts +3 -3
  13. package/dist/Model/Repository/internal/internal.d.ts.map +1 -1
  14. package/dist/Model/Repository/internal/internal.js +11 -7
  15. package/dist/Model/Repository/makeRepo.d.ts +2 -2
  16. package/dist/Model/Repository/makeRepo.d.ts.map +1 -1
  17. package/dist/Model/Repository/makeRepo.js +1 -1
  18. package/dist/Model/Repository/validation.d.ts +5 -4
  19. package/dist/Model/Repository/validation.d.ts.map +1 -1
  20. package/dist/Model/query/dsl.d.ts +9 -9
  21. package/dist/Operations.d.ts +2 -2
  22. package/dist/Operations.d.ts.map +1 -1
  23. package/dist/Operations.js +3 -3
  24. package/dist/OperationsRepo.d.ts +2 -2
  25. package/dist/OperationsRepo.d.ts.map +1 -1
  26. package/dist/OperationsRepo.js +3 -3
  27. package/dist/QueueMaker/SQLQueue.d.ts +3 -5
  28. package/dist/QueueMaker/SQLQueue.d.ts.map +1 -1
  29. package/dist/QueueMaker/SQLQueue.js +9 -7
  30. package/dist/QueueMaker/errors.d.ts +1 -1
  31. package/dist/QueueMaker/errors.d.ts.map +1 -1
  32. package/dist/QueueMaker/memQueue.d.ts.map +1 -1
  33. package/dist/QueueMaker/memQueue.js +10 -9
  34. package/dist/QueueMaker/sbqueue.d.ts.map +1 -1
  35. package/dist/QueueMaker/sbqueue.js +11 -9
  36. package/dist/RequestContext.d.ts +19 -14
  37. package/dist/RequestContext.d.ts.map +1 -1
  38. package/dist/RequestContext.js +5 -5
  39. package/dist/RequestFiberSet.d.ts +2 -2
  40. package/dist/RequestFiberSet.d.ts.map +1 -1
  41. package/dist/RequestFiberSet.js +5 -5
  42. package/dist/Store/ContextMapContainer.d.ts +14 -3
  43. package/dist/Store/ContextMapContainer.d.ts.map +1 -1
  44. package/dist/Store/ContextMapContainer.js +64 -3
  45. package/dist/Store/Cosmos.d.ts.map +1 -1
  46. package/dist/Store/Cosmos.js +55 -32
  47. package/dist/Store/Disk.d.ts.map +1 -1
  48. package/dist/Store/Disk.js +3 -4
  49. package/dist/Store/Memory.d.ts +2 -2
  50. package/dist/Store/Memory.d.ts.map +1 -1
  51. package/dist/Store/Memory.js +4 -4
  52. package/dist/Store/SQL/Pg.d.ts +4 -0
  53. package/dist/Store/SQL/Pg.d.ts.map +1 -0
  54. package/dist/Store/SQL/Pg.js +174 -0
  55. package/dist/Store/SQL/query.d.ts +34 -0
  56. package/dist/Store/SQL/query.d.ts.map +1 -0
  57. package/dist/Store/SQL/query.js +326 -0
  58. package/dist/Store/SQL.d.ts +4 -0
  59. package/dist/Store/SQL.d.ts.map +1 -0
  60. package/dist/Store/SQL.js +203 -0
  61. package/dist/Store/index.d.ts +1 -1
  62. package/dist/Store/index.d.ts.map +1 -1
  63. package/dist/Store/index.js +11 -1
  64. package/dist/Store/service.d.ts +8 -5
  65. package/dist/Store/service.d.ts.map +1 -1
  66. package/dist/Store/service.js +14 -6
  67. package/dist/adapters/SQL/Model.d.ts +2 -5
  68. package/dist/adapters/SQL/Model.d.ts.map +1 -1
  69. package/dist/adapters/SQL/Model.js +21 -13
  70. package/dist/adapters/ServiceBus.d.ts +6 -6
  71. package/dist/adapters/ServiceBus.d.ts.map +1 -1
  72. package/dist/adapters/ServiceBus.js +9 -9
  73. package/dist/adapters/cosmos-client.d.ts +2 -2
  74. package/dist/adapters/cosmos-client.d.ts.map +1 -1
  75. package/dist/adapters/cosmos-client.js +3 -3
  76. package/dist/adapters/logger.d.ts.map +1 -1
  77. package/dist/adapters/memQueue.d.ts +2 -2
  78. package/dist/adapters/memQueue.d.ts.map +1 -1
  79. package/dist/adapters/memQueue.js +3 -3
  80. package/dist/adapters/mongo-client.d.ts +2 -2
  81. package/dist/adapters/mongo-client.d.ts.map +1 -1
  82. package/dist/adapters/mongo-client.js +3 -3
  83. package/dist/adapters/redis-client.d.ts +3 -3
  84. package/dist/adapters/redis-client.d.ts.map +1 -1
  85. package/dist/adapters/redis-client.js +3 -3
  86. package/dist/api/ContextProvider.d.ts +6 -6
  87. package/dist/api/ContextProvider.d.ts.map +1 -1
  88. package/dist/api/ContextProvider.js +6 -6
  89. package/dist/api/internal/RequestContextMiddleware.d.ts +1 -1
  90. package/dist/api/internal/auth.d.ts +1 -1
  91. package/dist/api/internal/events.d.ts +2 -2
  92. package/dist/api/internal/events.d.ts.map +1 -1
  93. package/dist/api/internal/events.js +7 -5
  94. package/dist/api/layerUtils.d.ts +5 -5
  95. package/dist/api/layerUtils.d.ts.map +1 -1
  96. package/dist/api/layerUtils.js +5 -5
  97. package/dist/api/routing/middleware/RouterMiddleware.d.ts +3 -3
  98. package/dist/api/routing/middleware/RouterMiddleware.d.ts.map +1 -1
  99. package/dist/api/routing/middleware/middleware.d.ts +35 -1
  100. package/dist/api/routing/middleware/middleware.d.ts.map +1 -1
  101. package/dist/api/routing/middleware/middleware.js +39 -1
  102. package/dist/api/routing/schema/jwt.d.ts +1 -1
  103. package/dist/api/routing/schema/jwt.d.ts.map +1 -1
  104. package/dist/api/routing/schema/jwt.js +1 -1
  105. package/dist/api/routing.d.ts +1 -5
  106. package/dist/api/routing.d.ts.map +1 -1
  107. package/dist/api/routing.js +3 -2
  108. package/dist/api/setupRequest.d.ts +6 -3
  109. package/dist/api/setupRequest.d.ts.map +1 -1
  110. package/dist/api/setupRequest.js +11 -6
  111. package/dist/errorReporter.d.ts +1 -1
  112. package/dist/errorReporter.d.ts.map +1 -1
  113. package/dist/errorReporter.js +1 -1
  114. package/dist/fileUtil.js +1 -1
  115. package/dist/logger.d.ts.map +1 -1
  116. package/dist/rateLimit.js +1 -1
  117. package/examples/query.ts +29 -25
  118. package/package.json +32 -18
  119. package/src/CUPS.ts +2 -2
  120. package/src/Emailer/Sendgrid.ts +1 -1
  121. package/src/Emailer/service.ts +2 -2
  122. package/src/MainFiberSet.ts +2 -2
  123. package/src/Model/Repository/internal/internal.ts +11 -8
  124. package/src/Model/Repository/makeRepo.ts +2 -2
  125. package/src/Operations.ts +2 -2
  126. package/src/OperationsRepo.ts +2 -2
  127. package/src/QueueMaker/SQLQueue.ts +10 -10
  128. package/src/QueueMaker/memQueue.ts +41 -42
  129. package/src/QueueMaker/sbqueue.ts +65 -62
  130. package/src/RequestContext.ts +4 -4
  131. package/src/RequestFiberSet.ts +4 -4
  132. package/src/Store/ContextMapContainer.ts +98 -2
  133. package/src/Store/Cosmos.ts +207 -172
  134. package/src/Store/Disk.ts +2 -3
  135. package/src/Store/Memory.ts +4 -6
  136. package/src/Store/SQL/Pg.ts +294 -0
  137. package/src/Store/SQL/query.ts +372 -0
  138. package/src/Store/SQL.ts +327 -0
  139. package/src/Store/index.ts +10 -0
  140. package/src/Store/service.ts +16 -7
  141. package/src/adapters/SQL/Model.ts +76 -71
  142. package/src/adapters/ServiceBus.ts +8 -8
  143. package/src/adapters/cosmos-client.ts +2 -2
  144. package/src/adapters/memQueue.ts +2 -2
  145. package/src/adapters/mongo-client.ts +2 -2
  146. package/src/adapters/redis-client.ts +2 -2
  147. package/src/api/ContextProvider.ts +11 -11
  148. package/src/api/internal/events.ts +7 -6
  149. package/src/api/layerUtils.ts +8 -8
  150. package/src/api/routing/middleware/RouterMiddleware.ts +4 -4
  151. package/src/api/routing/middleware/middleware.ts +43 -0
  152. package/src/api/routing/schema/jwt.ts +2 -3
  153. package/src/api/routing.ts +7 -6
  154. package/src/api/setupRequest.ts +27 -7
  155. package/src/errorReporter.ts +1 -1
  156. package/src/fileUtil.ts +1 -1
  157. package/src/rateLimit.ts +2 -2
  158. package/test/contextProvider.test.ts +5 -5
  159. package/test/controller.test.ts +12 -9
  160. package/test/dist/contextProvider.test.d.ts.map +1 -1
  161. package/test/dist/controller.test.d.ts.map +1 -1
  162. package/test/dist/fixtures.d.ts +18 -8
  163. package/test/dist/fixtures.d.ts.map +1 -1
  164. package/test/dist/fixtures.js +11 -9
  165. package/test/dist/query.test.d.ts.map +1 -1
  166. package/test/dist/rawQuery.test.d.ts.map +1 -1
  167. package/test/dist/requires.test.d.ts.map +1 -1
  168. package/test/dist/rpc-multi-middleware.test.d.ts.map +1 -1
  169. package/test/dist/sql-store.test.d.ts.map +1 -0
  170. package/test/fixtures.ts +10 -8
  171. package/test/query.test.ts +160 -14
  172. package/test/rawQuery.test.ts +19 -17
  173. package/test/requires.test.ts +6 -5
  174. package/test/rpc-multi-middleware.test.ts +73 -4
  175. package/test/sql-store.test.ts +444 -0
  176. package/test/validateSample.test.ts +1 -1
  177. package/tsconfig.json +0 -1
@@ -9,6 +9,7 @@ import { InfraLogger } from "../logger.js"
9
9
  import type { FieldValues } from "../Model/filter/types.js"
10
10
  import { type RawQuery } from "../Model/query.js"
11
11
  import { buildWhereCosmosQuery3, logQuery } from "./Cosmos/query.js"
12
+ import { storeId } from "./Memory.js"
12
13
  import { type FilterArgs, type PersistenceModelType, type StorageConfig, type Store, type StoreConfig, StoreMaker } from "./service.js"
13
14
 
14
15
  const makeMapId =
@@ -50,7 +51,21 @@ function makeCosmosStore({ prefix }: StorageConfig) {
50
51
  }))
51
52
  )
52
53
 
53
- const mainPartitionKey = config?.partitionValue() ?? "primary"
54
+ const basePartitionKey = config?.partitionValue() ?? "primary"
55
+ const nsPrefix = (ns: string) => ns === "primary" ? "" : `${ns}::`
56
+ const nsPartitionValue = (ns: string, e?: Encoded) => {
57
+ const base = config?.partitionValue(e) ?? "primary"
58
+ return `${nsPrefix(ns)}${base}`
59
+ }
60
+ const resolveNamespace = !config?.allowNamespace
61
+ ? Effect.succeed("primary")
62
+ : storeId.asEffect().pipe(Effect.map((namespace) => {
63
+ if (namespace !== "primary" && !config.allowNamespace!(namespace)) {
64
+ throw new Error(`Namespace ${namespace} not allowed!`)
65
+ }
66
+ return namespace
67
+ }))
68
+ const resolvePartitionKey = Effect.map(resolveNamespace, (ns) => `${nsPrefix(ns)}${basePartitionKey}`)
54
69
 
55
70
  const defaultValues = config?.defaultValues ?? {}
56
71
  const container = db.container(containerId)
@@ -63,6 +78,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
63
78
  const bulkSet = (items: NonEmptyReadonlyArray<PM>) =>
64
79
  Effect
65
80
  .gen(function*() {
81
+ const ns = yield* resolveNamespace
66
82
  // TODO: disable batching if need atomicity
67
83
  // we delay and batch to keep low amount of RUs
68
84
  const b = [...items]
@@ -77,7 +93,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
77
93
  resourceBody: {
78
94
  ...Struct.omit(x, ["_etag", idKey]),
79
95
  id: x[idKey],
80
- _partitionKey: config?.partitionValue(x)
96
+ _partitionKey: nsPartitionValue(ns, x)
81
97
  }
82
98
  // don't use this or we get an error that the request and some item partition key dont match - makese no sense
83
99
  // partitionKey: config?.partitionValue(x)
@@ -89,7 +105,7 @@ function makeCosmosStore({ prefix }: StorageConfig) {
89
105
  resourceBody: {
90
106
  ...Struct.omit(x, ["_etag", idKey]),
91
107
  id: x[idKey],
92
- _partitionKey: config?.partitionValue(x)
108
+ _partitionKey: nsPartitionValue(ns, x)
93
109
  },
94
110
  ifMatch: eTag
95
111
  // don't use this or we get an error that the request and some item partition key dont match - makese no sense
@@ -166,65 +182,68 @@ function makeCosmosStore({ prefix }: StorageConfig) {
166
182
  }, { captureStackTrace: false }))
167
183
 
168
184
  const batchSet = (items: NonEmptyReadonlyArray<PM>) => {
169
- return Effect
170
- .suspend(() => {
171
- const batch = [...items].map(
172
- (x) =>
173
- [
174
- x,
175
- Option.match(Option.fromNullishOr(x._etag), {
176
- onNone: () => ({
177
- operationType: "Create" as const,
178
- resourceBody: {
179
- ...Struct.omit(x, ["_etag", idKey]),
180
- id: x[idKey],
181
- _partitionKey: config?.partitionValue(x)
182
- }
183
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
184
- // partitionKey: config?.partitionValue(x)
185
- }),
186
- onSome: (eTag) => ({
187
- operationType: "Replace" as const,
188
- id: x[idKey],
189
- resourceBody: {
190
- ...Struct.omit(x, ["_etag", idKey]),
191
- id: x[idKey],
192
- _partitionKey: config?.partitionValue(x)
193
- },
194
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
195
- // partitionKey: config?.partitionValue(x)
196
- ifMatch: eTag
197
- })
198
- })
199
- ] as const
200
- )
185
+ return resolveNamespace
186
+ .pipe(Effect.flatMap((ns) =>
187
+ Effect
188
+ .suspend(() => {
189
+ const batch = [...items].map(
190
+ (x) =>
191
+ [
192
+ x,
193
+ Option.match(Option.fromNullishOr(x._etag), {
194
+ onNone: () => ({
195
+ operationType: "Create" as const,
196
+ resourceBody: {
197
+ ...Struct.omit(x, ["_etag", idKey]),
198
+ id: x[idKey],
199
+ _partitionKey: nsPartitionValue(ns, x)
200
+ }
201
+ // don't use this or we get an error that the request and some item partition key dont match - makese no sense
202
+ // partitionKey: config?.partitionValue(x)
203
+ }),
204
+ onSome: (eTag) => ({
205
+ operationType: "Replace" as const,
206
+ id: x[idKey],
207
+ resourceBody: {
208
+ ...Struct.omit(x, ["_etag", idKey]),
209
+ id: x[idKey],
210
+ _partitionKey: nsPartitionValue(ns, x)
211
+ },
212
+ // don't use this or we get an error that the request and some item partition key dont match - makese no sense
213
+ // partitionKey: config?.partitionValue(x)
214
+ ifMatch: eTag
215
+ })
216
+ })
217
+ ] as const
218
+ )
201
219
 
202
- const ex = batch.map(([, c]) => c)
220
+ const ex = batch.map(([, c]) => c)
203
221
 
204
- return Effect
205
- .promise(() => execBatch(ex, ex[0]?.resourceBody._partitionKey))
206
- .pipe(Effect.flatMap(Effect.fnUntraced(function*(x) {
207
- const result = x.result ?? []
208
- const firstFailed = result.find(
209
- (x: any) => x.statusCode > 299 || x.statusCode < 200
210
- )
211
- if (firstFailed) {
212
- const code = firstFailed.statusCode ?? 0
213
- if (code === 412 || code === 404 || code === 409) {
214
- return yield* new OptimisticConcurrencyException({ type: name, id: "batch", code })
215
- }
222
+ return Effect
223
+ .promise(() => execBatch(ex, ex[0]?.resourceBody._partitionKey))
224
+ .pipe(Effect.flatMap(Effect.fnUntraced(function*(x) {
225
+ const result = x.result ?? []
226
+ const firstFailed = result.find(
227
+ (x: any) => x.statusCode > 299 || x.statusCode < 200
228
+ )
229
+ if (firstFailed) {
230
+ const code = firstFailed.statusCode ?? 0
231
+ if (code === 412 || code === 404 || code === 409) {
232
+ return yield* new OptimisticConcurrencyException({ type: name, id: "batch", code })
233
+ }
216
234
 
217
- return yield* Effect.die(
218
- new CosmosDbOperationError("not able to update record: " + code)
219
- )
220
- }
235
+ return yield* Effect.die(
236
+ new CosmosDbOperationError("not able to update record: " + code)
237
+ )
238
+ }
221
239
 
222
- return batch.map(([e], i) => ({
223
- ...e,
224
- _etag: result[i]?.eTag
225
- })) as unknown as NonEmptyReadonlyArray<Encoded>
226
- })))
227
- })
240
+ return batch.map(([e], i) => ({
241
+ ...e,
242
+ _etag: result[i]?.eTag
243
+ })) as unknown as NonEmptyReadonlyArray<Encoded>
244
+ })))
245
+ })
246
+ ))
228
247
  .pipe(Effect
229
248
  .withSpan("Cosmos.batchSet [effect-app/infra/Store]", {
230
249
  attributes: { "repository.container_id": containerId, "repository.model_name": name }
@@ -234,14 +253,14 @@ function makeCosmosStore({ prefix }: StorageConfig) {
234
253
  const s: Store<IdKey, Encoded> = {
235
254
  queryRaw: <Out>(query: RawQuery<Encoded, Out>) =>
236
255
  Effect
237
- .sync(() => query.cosmos({ name }))
256
+ .all({ q: Effect.sync(() => query.cosmos({ name })), pk: resolvePartitionKey })
238
257
  .pipe(
239
- Effect.tap((q) => logQuery(q)),
240
- Effect.flatMap((q) =>
258
+ Effect.tap(({ q }) => logQuery(q)),
259
+ Effect.flatMap(({ pk, q }) =>
241
260
  Effect.promise(() =>
242
261
  container
243
262
  .items
244
- .query<Out>(q, { partitionKey: mainPartitionKey })
263
+ .query<Out>(q, { partitionKey: pk })
245
264
  .fetchAll()
246
265
  .then(({ resources }) =>
247
266
  resources.map(
@@ -256,31 +275,36 @@ function makeCosmosStore({ prefix }: StorageConfig) {
256
275
  }, { captureStackTrace: false })
257
276
  ),
258
277
  batchRemove: (ids, partitionKey?: string) =>
259
- Effect.promise(() =>
260
- execBatch(
261
- mutable(ids.map((id) =>
262
- dropUndefinedT({
263
- operationType: "Delete" as const,
264
- id
265
- // don't use this or we get an error that the request and some item partition key dont match - makese no sense
266
- // partitionKey: config?.partitionValue({ [idKey]: id } as Encoded)
267
- })
268
- )),
269
- partitionKey ?? mainPartitionKey
278
+ resolvePartitionKey.pipe(Effect.flatMap((pk) =>
279
+ Effect.promise(() =>
280
+ execBatch(
281
+ mutable(ids.map((id) =>
282
+ dropUndefinedT({
283
+ operationType: "Delete" as const,
284
+ id
285
+ // don't use this or we get an error that the request and some item partition key dont match - makese no sense
286
+ // partitionKey: config?.partitionValue({ [idKey]: id } as Encoded)
287
+ })
288
+ )),
289
+ partitionKey ?? pk
290
+ )
270
291
  )
271
- ),
292
+ )),
272
293
  all: Effect
273
- .sync(() => ({
274
- query: `SELECT * FROM ${name}`,
275
- parameters: []
276
- }))
294
+ .all({
295
+ q: Effect.sync(() => ({
296
+ query: `SELECT * FROM ${name}`,
297
+ parameters: []
298
+ })),
299
+ pk: resolvePartitionKey
300
+ })
277
301
  .pipe(
278
- Effect.tap((q) => logQuery(q)),
279
- Effect.flatMap((q) =>
302
+ Effect.tap(({ q }) => logQuery(q)),
303
+ Effect.flatMap(({ pk, q }) =>
280
304
  Effect.promise(() =>
281
305
  container
282
306
  .items
283
- .query<PMCosmos>(q, { partitionKey: mainPartitionKey })
307
+ .query<PMCosmos>(q, { partitionKey: pk })
284
308
  .fetchAll()
285
309
  .then(({ resources }) =>
286
310
  resources.map(
@@ -305,42 +329,45 @@ function makeCosmosStore({ prefix }: StorageConfig) {
305
329
  const filter = f.filter
306
330
  type M = U extends undefined ? Encoded : Pick<Encoded, U>
307
331
  return Effect
308
- .sync(() =>
309
- buildWhereCosmosQuery3(
310
- idKey,
311
- filter ? [{ t: "where-scope", result: filter, relation: "some" }] : [],
312
- name,
313
- defaultValues,
314
- f.select as NonEmptyReadonlyArray<string | { key: string; subKeys: readonly string[] }> | undefined,
315
- f.order as NonEmptyReadonlyArray<{ key: string; direction: "ASC" | "DESC" }> | undefined,
316
- skip,
317
- limit
318
- )
319
- )
332
+ .all({
333
+ q: Effect.sync(() =>
334
+ buildWhereCosmosQuery3(
335
+ idKey,
336
+ filter ? [{ t: "where-scope", result: filter, relation: "some" }] : [],
337
+ name,
338
+ defaultValues,
339
+ f.select as
340
+ | NonEmptyReadonlyArray<string | { key: string; subKeys: readonly string[] }>
341
+ | undefined,
342
+ f.order as NonEmptyReadonlyArray<{ key: string; direction: "ASC" | "DESC" }> | undefined,
343
+ skip,
344
+ limit
345
+ )
346
+ ),
347
+ pk: resolvePartitionKey
348
+ })
320
349
  .pipe(
321
- Effect.tap((q) => logQuery(q)),
350
+ Effect.tap(({ q }) => logQuery(q)),
322
351
  Effect
323
- .flatMap((q) =>
352
+ .flatMap(({ pk, q }) =>
324
353
  Effect.promise(() =>
325
354
  f.select
326
355
  ? container
327
356
  .items
328
- .query<M>(q, { partitionKey: mainPartitionKey })
357
+ .query<M>(q, { partitionKey: pk })
329
358
  .fetchAll()
330
359
  .then(({ resources }) =>
331
- resources.map((_) =>
332
- ({
333
- ...pipe(
334
- defaultValues,
335
- Struct.pick(f.select!.filter((_) => typeof _ === "string"))
336
- ),
337
- ...mapReverseId(_ as any)
338
- }) as any
339
- )
360
+ resources.map((_) => ({
361
+ ...pipe(
362
+ defaultValues,
363
+ Struct.pick(f.select!.filter((_) => typeof _ === "string") as never[])
364
+ ),
365
+ ...mapReverseId(_ as any)
366
+ }))
340
367
  )
341
368
  : container
342
369
  .items
343
- .query<{ f: M }>(q, { partitionKey: mainPartitionKey })
370
+ .query<{ f: M }>(q, { partitionKey: pk })
344
371
  .fetchAll()
345
372
  .then(({ resources }) =>
346
373
  resources.map(({ f }) => ({ ...defaultValues, ...mapReverseId(f as any) }) as any)
@@ -355,78 +382,86 @@ function makeCosmosStore({ prefix }: StorageConfig) {
355
382
  )
356
383
  },
357
384
  find: (id) =>
358
- Effect
359
- .promise(() =>
360
- container
361
- .item(id, config?.partitionValue({ [idKey]: id } as Encoded))
362
- .read<Encoded>()
363
- .then(({ resource }) =>
364
- Option.fromNullishOr(resource).pipe(Option.map((_) => ({ ...defaultValues, ...mapReverseId(_) })))
365
- )
366
- )
367
- .pipe(Effect
368
- .withSpan("Cosmos.find [effect-app/infra/Store]", {
369
- attributes: {
370
- "repository.container_id": containerId,
371
- "repository.model_name": name,
372
- partitionValue: config?.partitionValue({ [idKey]: id } as Encoded),
373
- id
374
- }
375
- }, { captureStackTrace: false })),
376
- set: (e) =>
377
- Option
378
- .match(
379
- Option
380
- .fromNullishOr(e._etag),
381
- {
382
- onNone: () =>
383
- Effect.promise(() =>
384
- container.items.create({
385
- ...mapId(e),
386
- _partitionKey: config?.partitionValue(e)
387
- })
388
- ),
389
- onSome: (eTag) =>
390
- Effect.promise(() =>
391
- container.item(e[idKey], config?.partitionValue(e)).replace(
392
- { ...mapId(e), _partitionKey: config?.partitionValue(e) },
393
- {
394
- accessCondition: {
395
- type: "IfMatch",
396
- condition: eTag
397
- }
398
- }
385
+ resolveNamespace.pipe(Effect.flatMap((ns) =>
386
+ Effect
387
+ .promise(() =>
388
+ container
389
+ .item(id, nsPartitionValue(ns, { [idKey]: id } as Encoded))
390
+ .read<Encoded>()
391
+ .then(({ resource }) =>
392
+ Option.fromNullishOr(resource).pipe(
393
+ Option.map((_) => ({ ...defaultValues, ...mapReverseId(_) }))
399
394
  )
400
395
  )
401
- }
402
- )
403
- .pipe(
404
- Effect
405
- .flatMap((x) => {
406
- if (x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409) {
407
- return Effect.fail(new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode }))
408
- }
409
- if (x.statusCode > 299 || x.statusCode < 200) {
410
- return Effect.die(
411
- new CosmosDbOperationError(
412
- "not able to update record: " + x.statusCode
413
- )
414
- )
415
- }
416
- return Effect.sync(() => ({
417
- ...e,
418
- _etag: x.etag
419
- }))
420
- }),
421
- Effect
422
- .withSpan("Cosmos.set [effect-app/infra/Store]", {
396
+ )
397
+ .pipe(Effect
398
+ .withSpan("Cosmos.find [effect-app/infra/Store]", {
423
399
  attributes: {
424
400
  "repository.container_id": containerId,
425
401
  "repository.model_name": name,
426
- id: e[idKey]
402
+ partitionValue: nsPartitionValue(ns, { [idKey]: id } as Encoded),
403
+ id
427
404
  }
428
- }, { captureStackTrace: false })
429
- ),
405
+ }, { captureStackTrace: false }))
406
+ )),
407
+ set: (e) =>
408
+ resolveNamespace.pipe(Effect.flatMap((ns) =>
409
+ Option
410
+ .match(
411
+ Option
412
+ .fromNullishOr(e._etag),
413
+ {
414
+ onNone: () =>
415
+ Effect.promise(() =>
416
+ container.items.create({
417
+ ...mapId(e),
418
+ _partitionKey: nsPartitionValue(ns, e)
419
+ })
420
+ ),
421
+ onSome: (eTag) =>
422
+ Effect.promise(() =>
423
+ container.item(e[idKey], nsPartitionValue(ns, e)).replace(
424
+ { ...mapId(e), _partitionKey: nsPartitionValue(ns, e) },
425
+ {
426
+ accessCondition: {
427
+ type: "IfMatch",
428
+ condition: eTag
429
+ }
430
+ }
431
+ )
432
+ )
433
+ }
434
+ )
435
+ .pipe(
436
+ Effect
437
+ .flatMap((x) => {
438
+ if (x.statusCode === 412 || x.statusCode === 404 || x.statusCode === 409) {
439
+ return Effect.fail(
440
+ new OptimisticConcurrencyException({ type: name, id: e[idKey], code: x.statusCode })
441
+ )
442
+ }
443
+ if (x.statusCode > 299 || x.statusCode < 200) {
444
+ return Effect.die(
445
+ new CosmosDbOperationError(
446
+ "not able to update record: " + x.statusCode
447
+ )
448
+ )
449
+ }
450
+ return Effect.sync(() => ({
451
+ ...e,
452
+ _etag: x.etag
453
+ }))
454
+ }),
455
+ Effect
456
+ .withSpan("Cosmos.set [effect-app/infra/Store]", {
457
+ attributes: {
458
+ "repository.container_id": containerId,
459
+ "repository.model_name": name,
460
+ id: e[idKey]
461
+ }
462
+ }, { captureStackTrace: false })
463
+ )
464
+ )),
430
465
  batchSet,
431
466
  bulkSet
432
467
  }
package/src/Store/Disk.ts CHANGED
@@ -66,11 +66,10 @@ function makeDiskStoreInt<IdKey extends keyof Encoded, Encoded extends FieldValu
66
66
  }
67
67
 
68
68
  // lock file for cross-process coordination during initialization
69
- const lockFile = file + ".lock"
70
69
 
71
70
  // wrap initialization in file lock to prevent race conditions in multi-worker setups
72
71
  const store = yield* fu.withFileLock(
73
- lockFile,
72
+ file,
74
73
  Effect.gen(function*() {
75
74
  const shouldSeed = !(fs.existsSync(file))
76
75
 
@@ -143,7 +142,7 @@ export function makeDiskStore({ prefix }: StorageConfig, dir: string) {
143
142
  const storesSem = Semaphore.makeUnsafe(1)
144
143
  const primary = yield* makeDiskStoreInt(prefix, idKey, "primary", dir, name, seed, config?.defaultValues)
145
144
  const stores = new Map<string, Store<IdKey, Encoded>>([["primary", primary]])
146
- const ctx = yield* Effect.services<R>()
145
+ const ctx = yield* Effect.context<R>()
147
146
  const getStore = !config?.allowNamespace
148
147
  ? Effect.succeed(primary)
149
148
  : storeId.asEffect().pipe(Effect.flatMap((namespace) => {
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
 
3
- import { Array, Effect, flow, type NonEmptyReadonlyArray, Option, Order, pipe, Ref, Result, Semaphore, ServiceMap, Struct } from "effect-app"
3
+ import { Array, Context, Effect, flow, type NonEmptyReadonlyArray, Option, Order, pipe, Ref, Result, Semaphore, Struct } from "effect-app"
4
4
  import { NonEmptyString255 } from "effect-app/Schema"
5
5
  import { get } from "effect-app/utils"
6
6
  import { InfraLogger } from "../logger.js"
@@ -24,7 +24,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
24
24
  )
25
25
  const n = Struct.pick(i, keys)
26
26
  subKeys.forEach((subKey) => {
27
- n[subKey.key] = i[subKey.key]!.map(Struct.pick(subKey.subKeys))
27
+ n[subKey.key] = i[subKey.key]!.map(Struct.pick(subKey.subKeys as never[]))
28
28
  })
29
29
  return n as M
30
30
  }) as any
@@ -72,9 +72,7 @@ export function memFilter<T extends FieldValues, U extends keyof T = never>(f: F
72
72
  }
73
73
 
74
74
  const defaultNs: NonEmptyString255 = NonEmptyString255("primary")
75
- export class storeId
76
- extends ServiceMap.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs })
77
- {}
75
+ export class storeId extends Context.Reference("StoreId", { defaultValue: (): NonEmptyString255 => defaultNs }) {}
78
76
 
79
77
  function logQuery(f: FilterArgs<any, any>, defaultValues?: any) {
80
78
  return InfraLogger
@@ -267,7 +265,7 @@ export const makeMemoryStore = () => ({
267
265
  seed,
268
266
  config?.defaultValues
269
267
  )
270
- const ctx = yield* Effect.services<R>()
268
+ const ctx = yield* Effect.context<R>()
271
269
  const stores = new Map([["primary", primary]])
272
270
  const getStore = !config?.allowNamespace
273
271
  ? Effect.succeed(primary)