@effect-app/infra 1.36.0 → 1.38.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 (34) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/_cjs/services/RepositoryBase.cjs +6 -1
  3. package/_cjs/services/RepositoryBase.cjs.map +1 -1
  4. package/_cjs/services/Store/Cosmos/query.cjs +14 -2
  5. package/_cjs/services/Store/Cosmos/query.cjs.map +1 -1
  6. package/_cjs/services/Store/Memory.cjs.map +1 -1
  7. package/_cjs/services/Store/codeFilter.cjs +12 -0
  8. package/_cjs/services/Store/codeFilter.cjs.map +1 -1
  9. package/dist/RequestContext.d.ts +3 -3
  10. package/dist/api/internal/RequestContextMiddleware.d.ts +1 -1
  11. package/dist/api/reportError.d.ts +2 -2
  12. package/dist/services/Repository/ext.d.ts +4 -4
  13. package/dist/services/RepositoryBase.d.ts +2 -2
  14. package/dist/services/RepositoryBase.d.ts.map +1 -1
  15. package/dist/services/RepositoryBase.js +5 -5
  16. package/dist/services/RequestContextContainer.d.ts +1 -1
  17. package/dist/services/Store/Cosmos/query.d.ts.map +1 -1
  18. package/dist/services/Store/Cosmos/query.js +14 -3
  19. package/dist/services/Store/Memory.d.ts.map +1 -1
  20. package/dist/services/Store/Memory.js +2 -4
  21. package/dist/services/Store/codeFilter.d.ts.map +1 -1
  22. package/dist/services/Store/codeFilter.js +12 -1
  23. package/dist/services/Store/filterApi/query.d.ts +1 -1
  24. package/dist/services/Store/filterApi/query.d.ts.map +1 -1
  25. package/dist/services/Store/service.d.ts +1 -1
  26. package/dist/services/query/dsl.d.ts +7 -0
  27. package/dist/services/query/dsl.d.ts.map +1 -1
  28. package/package.json +10 -10
  29. package/src/services/RepositoryBase.ts +8 -5
  30. package/src/services/Store/Cosmos/query.ts +17 -2
  31. package/src/services/Store/Memory.ts +2 -4
  32. package/src/services/Store/codeFilter.ts +12 -0
  33. package/src/services/Store/filterApi/query.ts +4 -1
  34. package/src/services/query/dsl.ts +95 -3
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@effect-app/infra",
3
- "version": "1.36.0",
3
+ "version": "1.38.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "dependencies": {
7
7
  "@azure/cosmos": "^4.1.1",
8
8
  "@azure/service-bus": "^7.9.5",
9
- "@effect/rpc": "^0.43.3",
10
- "@effect/rpc-http": "^0.41.3",
9
+ "@effect/rpc": "^0.43.4",
10
+ "@effect/rpc-http": "^0.41.4",
11
11
  "express-oauth2-jwt-bearer": "^1.6.0",
12
12
  "@faker-js/faker": "^8.4.1",
13
13
  "@sendgrid/helpers": "^8.0.0",
@@ -20,10 +20,10 @@
20
20
  "proper-lockfile": "^4.1.2",
21
21
  "pure-rand": "6.1.0",
22
22
  "redlock": "^4.2.0",
23
- "@effect-app/infra-adapters": "1.18.0",
24
- "effect-app": "1.27.0",
25
- "@effect-app/core": "1.16.0",
26
- "@effect-app/schema": "1.18.0"
23
+ "@effect-app/core": "1.17.0",
24
+ "effect-app": "1.28.0",
25
+ "@effect-app/schema": "1.19.0",
26
+ "@effect-app/infra-adapters": "1.19.0"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@babel/cli": "^7.25.7",
@@ -39,13 +39,13 @@
39
39
  "mongodb": "6.9.0",
40
40
  "redis": "^3.1.2",
41
41
  "typescript": "^5.6.3",
42
- "vitest": "^2.1.2"
42
+ "vitest": "^2.1.3"
43
43
  },
44
44
  "peerDependencies": {
45
45
  "express": "^4.21.1",
46
- "@effect/platform": "^0.68.4",
46
+ "@effect/platform": "^0.68.5",
47
47
  "@effect/schema": "^0.75.4",
48
- "@effect/sql": "^0.16.4",
48
+ "@effect/sql": "^0.16.5",
49
49
  "@effect/vitest": "^0.12.1",
50
50
  "effect": "^3.9.2"
51
51
  },
@@ -525,11 +525,12 @@ export function makeRepo<
525
525
  .pipe(Effect.orDie, Effect.withSpan("parseMany2", { captureStackTrace: false })))
526
526
  const filter = <U extends keyof Encoded = keyof Encoded>(args: FilterArgs<Encoded, U>) =>
527
527
  store
528
- .filter(args)
528
+ .filter(
529
+ // always enforce id and _etag because they are system fields, required for etag tracking etc
530
+ { ...args, select: args.select ? [...args.select, "id", "_etag" as any] : undefined } as typeof args
531
+ )
529
532
  .pipe(Effect.tap((items) =>
530
- args.select
531
- ? Effect.void
532
- : Effect.map(cms, ({ set }) => items.forEach((_) => set((_ as Encoded).id, (_ as PM)._etag)))
533
+ Effect.map(cms, ({ set }) => items.forEach((_) => set((_ as Encoded).id, (_ as PM)._etag)))
533
534
  ))
534
535
 
535
536
  // TODO: For raw we should use S.from, and drop the R...
@@ -543,7 +544,9 @@ export function makeRepo<
543
544
  const eff = a.mode === "project"
544
545
  ? filter(a)
545
546
  // TODO: mapFrom but need to support per field and dependencies
546
- .pipe(Effect.andThen(flow(S.decode(S.Array(a.schema ?? schema)), Effect.provide(rctx))))
547
+ .pipe(
548
+ Effect.andThen(flow(S.decode(S.Array(a.schema ?? schema)), Effect.provide(rctx)))
549
+ )
547
550
  : a.mode === "collect"
548
551
  ? filter(a)
549
552
  // TODO: mapFrom but need to support per field and dependencies
@@ -1,3 +1,4 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
1
2
  import { Array, Effect, Equivalence, pipe } from "effect-app"
2
3
  import type { NonEmptyReadonlyArray } from "effect-app"
3
4
  import { assertUnreachable } from "effect-app/utils"
@@ -27,6 +28,9 @@ export function logQuery(q: {
27
28
  }))
28
29
  }
29
30
 
31
+ const arrayContains = (v: any[]) => v.map((_) => JSON.stringify(_)).join(", ")
32
+ const vAsArr = (v: string) => v as unknown as any[]
33
+
30
34
  export function buildWhereCosmosQuery3(
31
35
  filter: readonly FilterResult[],
32
36
  name: string,
@@ -53,9 +57,20 @@ export function buildWhereCosmosQuery3(
53
57
  return `(NOT ARRAY_CONTAINS(${v}, ${k}))`
54
58
 
55
59
  case "includes":
56
- return `ARRAY_CONTAINS(${k}, ${v})`
60
+ return `ARRAY_CONTAINS(${k}, ${arrayContains(vAsArr(v))})`
57
61
  case "notIncludes":
58
- return `(NOT ARRAY_CONTAINS(${k}, ${v}))`
62
+ return `(NOT ARRAY_CONTAINS(${k}, ${arrayContains(vAsArr(v))}))`
63
+
64
+ case "includes-any":
65
+ return `ARRAY_CONTAINS_ANY(${k}, ${arrayContains(vAsArr(v))})`
66
+ case "notIncludes-any":
67
+ return `(NOT ARRAY_CONTAINS_ANY(${k}, ${arrayContains(vAsArr(v))}))`
68
+
69
+ case "includes-all":
70
+ return `ARRAY_CONTAINS_ALL(${k}, ${arrayContains(vAsArr(v))})`
71
+ case "notIncludes-all":
72
+ return `(NOT ARRAY_CONTAINS_ALL(${k}, ${arrayContains(vAsArr(v))}))`
73
+
59
74
  case "contains":
60
75
  return `CONTAINS(${k}, ${v}, true)`
61
76
 
@@ -240,10 +240,8 @@ export const makeMemoryStore = () => ({
240
240
  return makeMemoryStoreInt(modelName, namespace, seed, config?.defaultValues)
241
241
  .pipe(
242
242
  Effect.orDie,
243
- Effect
244
- .provide(ctx),
245
- Effect
246
- .tap((store) => Effect.sync(() => stores.set(namespace, store)))
243
+ Effect.provide(ctx),
244
+ Effect.tap((store) => Effect.sync(() => stores.set(namespace, store)))
247
245
  )
248
246
  }))
249
247
  }))
@@ -1,8 +1,12 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
1
3
  import { Array } from "effect-app"
2
4
  import { assertUnreachable, get } from "effect-app/utils"
3
5
  import type { FilterR, FilterResult } from "./filterApi/query.js"
4
6
  import { compare, greaterThan, greaterThanExclusive, lowerThan, lowerThanExclusive } from "./utils.js"
5
7
 
8
+ const vAsArr = (v: string) => v as unknown as any[]
9
+
6
10
  export const codeFilterStatement = <E>(p: FilterR, x: E) => {
7
11
  const k = get(x, p.path)
8
12
  switch (p.op) {
@@ -22,6 +26,14 @@ export const codeFilterStatement = <E>(p: FilterR, x: E) => {
22
26
  return (k as Array<string>).includes(p.value)
23
27
  case "notIncludes":
24
28
  return !(k as Array<string>).includes(p.value)
29
+ case "includes-any":
30
+ return (vAsArr(p.value)).some((_) => (k as Array<string>)?.includes(_))
31
+ case "notIncludes-any":
32
+ return !(vAsArr(p.value)).some((_) => (k as Array<string>)?.includes(_))
33
+ case "includes-all":
34
+ return (vAsArr(p.value)).every((_) => (k as Array<string>)?.includes(_))
35
+ case "notIncludes-all":
36
+ return !(vAsArr(p.value)).every((_) => (k as Array<string>)?.includes(_))
25
37
  case "contains":
26
38
  return (k as string).toLowerCase().includes(p.value.toLowerCase())
27
39
  case "endsWith":
@@ -11,6 +11,10 @@ export type OtherOps =
11
11
  | "notContains"
12
12
  | "includes"
13
13
  | "notIncludes"
14
+ | "includes-any"
15
+ | "notIncludes-any"
16
+ | "includes-all"
17
+ | "notIncludes-all"
14
18
  | "eq"
15
19
  | "neq"
16
20
  | "gt"
@@ -18,7 +22,6 @@ export type OtherOps =
18
22
  | "lt"
19
23
  | "lte"
20
24
 
21
- // TODO: includes | notIncludes
22
25
  export type Ops = OtherOps | InOps
23
26
 
24
27
  export type FilterScopes = {
@@ -316,11 +316,41 @@ export type FilterWheres = {
316
316
  V extends FieldPathValue<TFieldValues, TFieldName>
317
317
  >(
318
318
  path: TFieldName,
319
- op: "in" | "notIn",
319
+ op:
320
+ | "in"
321
+ | "notIn",
320
322
  value: readonly V[]
321
323
  ): (
322
324
  current: Query<TFieldValues>
323
325
  ) => QueryWhere<TFieldValues>
326
+ <
327
+ TFieldValues extends FieldValues,
328
+ TFieldName extends FieldPath<TFieldValues>,
329
+ V extends FieldPathValue<TFieldValues, TFieldName>
330
+ >(
331
+ path: TFieldName,
332
+ op:
333
+ | "includes"
334
+ | "notIncludes",
335
+ value: GetArV<V>
336
+ ): (
337
+ current: Query<TFieldValues>
338
+ ) => QueryWhere<TFieldValues>
339
+ <
340
+ TFieldValues extends FieldValues,
341
+ TFieldName extends FieldPath<TFieldValues>,
342
+ V extends FieldPathValue<TFieldValues, TFieldName>
343
+ >(
344
+ path: TFieldName,
345
+ op:
346
+ | "includes-any"
347
+ | "notIncludes-any"
348
+ | "includes-all"
349
+ | "notIncludes-all",
350
+ value: readonly GetArV<V>[]
351
+ ): (
352
+ current: Query<TFieldValues>
353
+ ) => QueryWhere<TFieldValues>
324
354
  }
325
355
 
326
356
  export type FilterWhere =
@@ -504,11 +534,41 @@ export type FilterWhere =
504
534
  V extends FieldPathValue<TFieldValues, TFieldName>
505
535
  >(
506
536
  path: TFieldName,
507
- op: "in" | "notIn",
537
+ op:
538
+ | "in"
539
+ | "notIn",
508
540
  value: readonly V[]
509
541
  ): (
510
542
  current: Query<TFieldValues>
511
543
  ) => QueryWhere<TFieldValues>
544
+ <
545
+ TFieldValues extends FieldValues,
546
+ TFieldName extends FieldPath<TFieldValues>,
547
+ V extends FieldPathValue<TFieldValues, TFieldName>
548
+ >(
549
+ path: TFieldName,
550
+ op:
551
+ | "includes"
552
+ | "notIncludes",
553
+ value: GetArV<V>
554
+ ): (
555
+ current: Query<TFieldValues>
556
+ ) => QueryWhere<TFieldValues>
557
+ <
558
+ TFieldValues extends FieldValues,
559
+ TFieldName extends FieldPath<TFieldValues>,
560
+ V extends FieldPathValue<TFieldValues, TFieldName>
561
+ >(
562
+ path: TFieldName,
563
+ op:
564
+ | "includes-any"
565
+ | "notIncludes-any"
566
+ | "includes-all"
567
+ | "notIncludes-all",
568
+ value: readonly GetArV<V>[]
569
+ ): (
570
+ current: Query<TFieldValues>
571
+ ) => QueryWhere<TFieldValues>
512
572
  }
513
573
 
514
574
  export type FilterContinuations = {
@@ -562,13 +622,45 @@ export type FilterContinuations = {
562
622
  V extends FieldPathValue<TFieldValues, TFieldName>
563
623
  >(
564
624
  path: TFieldName,
565
- op: "in" | "notIn",
625
+ op:
626
+ | "in"
627
+ | "notIn",
566
628
  value: readonly V[]
567
629
  ): (
568
630
  current: QueryWhere<TFieldValues>
569
631
  ) => QueryWhere<TFieldValues>
632
+ <
633
+ TFieldValues extends FieldValues,
634
+ TFieldName extends FieldPath<TFieldValues>,
635
+ V extends FieldPathValue<TFieldValues, TFieldName>
636
+ >(
637
+ path: TFieldName,
638
+ op:
639
+ | "includes"
640
+ | "notIncludes",
641
+ value: GetArV<V>
642
+ ): (
643
+ current: QueryWhere<TFieldValues>
644
+ ) => QueryWhere<TFieldValues>
645
+ <
646
+ TFieldValues extends FieldValues,
647
+ TFieldName extends FieldPath<TFieldValues>,
648
+ V extends FieldPathValue<TFieldValues, TFieldName>
649
+ >(
650
+ path: TFieldName,
651
+ op:
652
+ | "includes-any"
653
+ | "notIncludes-any"
654
+ | "includes-all"
655
+ | "notIncludes-all",
656
+ value: readonly GetArV<V>[]
657
+ ): (
658
+ current: QueryWhere<TFieldValues>
659
+ ) => QueryWhere<TFieldValues>
570
660
  }
571
661
 
662
+ type GetArV<T> = T extends readonly (infer R)[] ? R : never
663
+
572
664
  export type FilterContinuationClosure = {
573
665
  <TFieldValues extends FieldValues>(
574
666
  fb: (current: Query<TFieldValues>) => QueryWhere<TFieldValues>