@effect-app/infra 0.118.0 → 0.120.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 (88) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/_cjs/services/Emailer/Sendgrid.cjs +1 -1
  3. package/_cjs/services/Emailer/Sendgrid.cjs.map +1 -1
  4. package/_cjs/services/Repository.cjs +4 -38
  5. package/_cjs/services/Repository.cjs.map +1 -1
  6. package/_cjs/services/RepositoryBase.cjs +39 -36
  7. package/_cjs/services/RepositoryBase.cjs.map +1 -1
  8. package/_cjs/services/Store/Cosmos/query.cjs +291 -0
  9. package/_cjs/services/Store/Cosmos/query.cjs.map +1 -0
  10. package/_cjs/services/Store/Cosmos.cjs +38 -183
  11. package/_cjs/services/Store/Cosmos.cjs.map +1 -1
  12. package/_cjs/services/Store/Cosmos.test.cjs +7 -7
  13. package/_cjs/services/Store/Cosmos.test.cjs.map +1 -1
  14. package/_cjs/services/Store/Disk.cjs +1 -1
  15. package/_cjs/services/Store/Disk.cjs.map +1 -1
  16. package/_cjs/services/Store/Memory.cjs +20 -17
  17. package/_cjs/services/Store/Memory.cjs.map +1 -1
  18. package/_cjs/services/Store/Redis.cjs +2 -2
  19. package/_cjs/services/Store/Redis.cjs.map +1 -1
  20. package/_cjs/services/Store/codeFilter.cjs +151 -0
  21. package/_cjs/services/Store/codeFilter.cjs.map +1 -0
  22. package/_cjs/services/Store/filterApi/proxy.cjs +40 -0
  23. package/_cjs/services/Store/filterApi/proxy.cjs.map +1 -0
  24. package/_cjs/services/Store/filterApi/query.cjs +173 -0
  25. package/_cjs/services/Store/filterApi/query.cjs.map +1 -0
  26. package/_cjs/services/Store/filterApi.test.cjs +164 -0
  27. package/_cjs/services/Store/filterApi.test.cjs.map +1 -0
  28. package/_cjs/services/Store/index.cjs +1 -1
  29. package/_cjs/services/Store/index.cjs.map +1 -1
  30. package/_cjs/services/Store/service.cjs.map +1 -1
  31. package/_cjs/services/Store/utils.cjs +8 -1
  32. package/_cjs/services/Store/utils.cjs.map +1 -1
  33. package/_src/services/Emailer/Sendgrid.ts +0 -1
  34. package/_src/services/Repository.ts +124 -70
  35. package/_src/services/RepositoryBase.ts +26 -12
  36. package/_src/services/Store/Cosmos/query.ts +390 -0
  37. package/_src/services/Store/Cosmos.test.ts +5 -5
  38. package/_src/services/Store/Cosmos.ts +39 -245
  39. package/_src/services/Store/Disk.ts +0 -1
  40. package/_src/services/Store/Memory.ts +21 -18
  41. package/_src/services/Store/Redis.ts +3 -4
  42. package/_src/services/Store/codeFilter.ts +150 -0
  43. package/_src/services/Store/filterApi/proxy.ts +109 -0
  44. package/_src/services/Store/filterApi/query.ts +256 -0
  45. package/_src/services/Store/filterApi.test.ts +263 -0
  46. package/_src/services/Store/index.ts +0 -1
  47. package/_src/services/Store/service.ts +17 -5
  48. package/_src/services/Store/utils.ts +10 -7
  49. package/dist/services/Emailer/Sendgrid.d.ts.map +1 -1
  50. package/dist/services/Emailer/Sendgrid.js +2 -2
  51. package/dist/services/Repository.d.ts +44 -104
  52. package/dist/services/Repository.d.ts.map +1 -1
  53. package/dist/services/Repository.js +6 -30
  54. package/dist/services/RepositoryBase.d.ts +11 -21
  55. package/dist/services/RepositoryBase.d.ts.map +1 -1
  56. package/dist/services/RepositoryBase.js +41 -37
  57. package/dist/services/Store/Cosmos/query.d.ts +61 -0
  58. package/dist/services/Store/Cosmos/query.d.ts.map +1 -0
  59. package/dist/services/Store/Cosmos/query.js +302 -0
  60. package/dist/services/Store/Cosmos.d.ts +3 -47
  61. package/dist/services/Store/Cosmos.d.ts.map +1 -1
  62. package/dist/services/Store/Cosmos.js +39 -193
  63. package/dist/services/Store/Disk.d.ts.map +1 -1
  64. package/dist/services/Store/Disk.js +2 -2
  65. package/dist/services/Store/Memory.d.ts +2 -7
  66. package/dist/services/Store/Memory.d.ts.map +1 -1
  67. package/dist/services/Store/Memory.js +17 -15
  68. package/dist/services/Store/Redis.d.ts.map +1 -1
  69. package/dist/services/Store/Redis.js +3 -3
  70. package/dist/services/Store/codeFilter.d.ts +5 -0
  71. package/dist/services/Store/codeFilter.d.ts.map +1 -0
  72. package/dist/services/Store/codeFilter.js +140 -0
  73. package/dist/services/Store/filterApi/proxy.d.ts +41 -0
  74. package/dist/services/Store/filterApi/proxy.d.ts.map +1 -0
  75. package/dist/services/Store/filterApi/proxy.js +41 -0
  76. package/dist/services/Store/filterApi/query.d.ts +64 -0
  77. package/dist/services/Store/filterApi/query.d.ts.map +1 -0
  78. package/dist/services/Store/filterApi/query.js +134 -0
  79. package/dist/services/Store/filterApi.test.d.ts.map +1 -0
  80. package/dist/services/Store/index.d.ts.map +1 -1
  81. package/dist/services/Store/index.js +2 -2
  82. package/dist/services/Store/service.d.ts +12 -5
  83. package/dist/services/Store/service.d.ts.map +1 -1
  84. package/dist/services/Store/service.js +1 -1
  85. package/dist/services/Store/utils.d.ts +7 -1
  86. package/dist/services/Store/utils.d.ts.map +1 -1
  87. package/dist/services/Store/utils.js +118 -115
  88. package/package.json +48 -8
@@ -0,0 +1,256 @@
1
+ import type { FieldValues } from "../../../filter/types.js"
2
+ import type { FieldPath, FieldPathValue } from "../../../filter/types/path/eager.js"
3
+ import type { Filter, Ops } from "./proxy.js"
4
+ import { makeProxy } from "./proxy.js"
5
+
6
+ export type FilterScopes = {
7
+ t: "or-scope"
8
+ result: FilterResult[]
9
+ } | {
10
+ t: "and-scope"
11
+ result: FilterResult[]
12
+ } | {
13
+ t: "where-scope"
14
+ result: FilterResult[]
15
+ }
16
+
17
+ export type FilterR = {
18
+ op: Ops
19
+
20
+ path: string
21
+ value: string // ToDO: Value[]
22
+ }
23
+
24
+ export type FilterResult =
25
+ | {
26
+ t: "where"
27
+ } & FilterR
28
+ | {
29
+ t: "or"
30
+ } & FilterR
31
+ | {
32
+ t: "and"
33
+ } & FilterR
34
+ | FilterScopes
35
+
36
+ export const print = (state: readonly FilterResult[]) => {
37
+ let s = ""
38
+ let l = 0
39
+ const printN = (n: number) => {
40
+ return n === 0 ? "" : ReadonlyArray.range(1, n).map(() => " ").join("")
41
+ }
42
+ for (const e of state) {
43
+ switch (e.t) {
44
+ case "where":
45
+ s += `${e.path} ${e.op} ${e.value}`
46
+ break
47
+ case "or":
48
+ s += ` OR ${e.path} ${e.op} ${e.value}`
49
+ break
50
+ case "and":
51
+ s += ` AND ${e.path} ${e.op} ${e.value}`
52
+ break
53
+ case "or-scope": {
54
+ ;++l
55
+ s += ` OR (\n${printN(l + 1)}${print(e.result)}\n${printN(l)})`
56
+ ;--l
57
+ break
58
+ }
59
+ case "and-scope": {
60
+ ;++l
61
+ s += ` AND (\n${printN(l + 1)}${print(e.result)}\n${printN(l)})`
62
+ ;--l
63
+
64
+ break
65
+ }
66
+ case "where-scope": {
67
+ // ;++l
68
+ s += `(\n${printN(l + 1)}${print(e.result)}\n)`
69
+ // ;--l
70
+ break
71
+ }
72
+ }
73
+ }
74
+ return s
75
+ }
76
+
77
+ export const makeFilter = <TFieldValues extends FieldValues>() => {
78
+ // const recursive = {}
79
+ // const and = Object.assign(() => {}, recursive)
80
+ // const or = Object.assign(() => {}, recursive)
81
+ // const where = Object.assign(() => {}, recursive)
82
+ // recursive.and = and
83
+ // recursive.or = or
84
+ // recursive.where = where
85
+ const state: FilterResult[] = []
86
+ let current = state
87
+ const where = (...args: any[]) => {
88
+ if (typeof args[0] === "function") {
89
+ const mine: any[] = []
90
+ const previous = current
91
+ current = mine
92
+ const r = args[0](all)
93
+ current = previous
94
+ current.push({ t: "where-scope", result: mine })
95
+ return r
96
+ }
97
+ if (args.length === 1) {
98
+ current.push({ t: "where", ...args[0] })
99
+ return all
100
+ }
101
+ current.push({
102
+ t: "where",
103
+ op: args.length === 2 ? "eq" : args[1],
104
+ path: args[0],
105
+ value: args[args.length - 1]
106
+ })
107
+ return all
108
+ }
109
+ const and = (...args: any[]) => {
110
+ if (typeof args[0] === "function") {
111
+ const mine: any[] = []
112
+ const previous = current
113
+ current = mine
114
+ const r = args[0](all)
115
+ current = previous
116
+ current.push({ t: "and-scope", result: mine })
117
+ return r
118
+ }
119
+ if (args.length === 1) {
120
+ current.push({ t: "and", ...args[0] })
121
+ return all
122
+ }
123
+ current.push({
124
+ t: "and",
125
+ op: args.length === 2 ? "eq" : args[1],
126
+ path: args[0],
127
+ value: args[args.length - 1]
128
+ })
129
+ return all
130
+ }
131
+ const or = (...args: any[]) => {
132
+ if (typeof args[0] === "function") {
133
+ const mine: any[] = []
134
+ const previous = current
135
+ current = mine
136
+ const r = args[0](all)
137
+ current = previous
138
+ current.push({ t: "or-scope", result: mine })
139
+ return r
140
+ }
141
+ if (args.length === 1) {
142
+ current.push({ t: "or", ...args[0] })
143
+ return all
144
+ }
145
+ current.push({ t: "or", op: args.length === 2 ? "eq" : args[1], path: args[0], value: args[args.length - 1] })
146
+ return all
147
+ }
148
+ const fields = makeProxy() as Filter<TFieldValues>
149
+ const all = Object.assign(where, {
150
+ type: "new-kid",
151
+ and,
152
+ or,
153
+ build() {
154
+ return state
155
+ }
156
+ })
157
+ return { all, fields }
158
+ }
159
+
160
+ export const QueryBuilder = {
161
+ make: <TFieldValues extends FieldValues>(): (
162
+ fn: (f: FilterTest<TFieldValues>, fields: Filter<TFieldValues>) => QueryBuilder<TFieldValues>
163
+ ) => QueryBuilder<TFieldValues> => {
164
+ type F = ReturnType<typeof makeFilter<TFieldValues>>
165
+ return (
166
+ f: (
167
+ n: F["all"],
168
+ f: F["fields"]
169
+ ) => QueryBuilder<TFieldValues>
170
+ ) => {
171
+ const fil = makeFilter<TFieldValues>()
172
+ return f(fil.all, fil.fields)
173
+ }
174
+ }
175
+ }
176
+
177
+ export type Initial<TFieldValues extends FieldValues> = {
178
+ where: FilterTest<TFieldValues>
179
+ build(): FilterResult[]
180
+ }
181
+
182
+ export type Filts<TFieldValues extends FieldValues> = {
183
+ <
184
+ TFieldName extends FieldPath<TFieldValues>,
185
+ V extends FieldPathValue<TFieldValues, TFieldName>
186
+ >(
187
+ path: TFieldName,
188
+ value: V
189
+ ): QueryBuilder<TFieldValues>
190
+ <
191
+ TFieldName extends FieldPath<TFieldValues>,
192
+ V extends FieldPathValue<TFieldValues, TFieldName>
193
+ >(
194
+ path: TFieldName,
195
+ op: "neq",
196
+ value: V
197
+ ): QueryBuilder<TFieldValues>
198
+ <
199
+ TFieldName extends FieldPath<TFieldValues>,
200
+ V extends FieldPathValue<TFieldValues, TFieldName>
201
+ >(
202
+ path: TFieldName,
203
+ op: "gt" | "gte" | "lt" | "lte",
204
+ value: V // only numbers?
205
+ ): QueryBuilder<TFieldValues>
206
+ <
207
+ TFieldName extends FieldPath<TFieldValues>,
208
+ V extends FieldPathValue<TFieldValues, TFieldName>
209
+ >(
210
+ path: TFieldName,
211
+ op: "startsWith" | "endsWith" | "contains" | "notContains" | "notStartsWith" | "notEndsWith",
212
+ value: V // only strings?
213
+ ): QueryBuilder<TFieldValues>
214
+ <
215
+ TFieldName extends FieldPath<TFieldValues>,
216
+ V extends FieldPathValue<TFieldValues, TFieldName>
217
+ >(
218
+ path: TFieldName,
219
+ op: "in" | "notIn",
220
+ value: readonly V[]
221
+ ): QueryBuilder<TFieldValues>
222
+ }
223
+
224
+ // type Filter<TFieldValues extends FieldValues> = {
225
+ // (
226
+ // fb: (f: Filts<TFieldValues>) => QueryBuilder<TFieldValues>
227
+ // ): QueryBuilder<TFieldValues>
228
+ // } & Filts<TFieldValues>
229
+
230
+ export type FilterTest<TFieldValues extends FieldValues> = {
231
+ (
232
+ fb: (f: Filts<TFieldValues> & Initial<TFieldValues>) => QueryBuilder<TFieldValues>
233
+ ): QueryBuilder<TFieldValues>
234
+ <
235
+ TFieldName extends FieldPath<TFieldValues>,
236
+ V extends FieldPathValue<TFieldValues, TFieldName>
237
+ >(f: {
238
+ path: TFieldName
239
+ op: Ops
240
+ value: V
241
+ }): QueryBuilder<TFieldValues>
242
+ } & Filts<TFieldValues>
243
+ // const not = <A extends string>(s: A) => `!${s}`
244
+
245
+ // type FilterGroup<TFieldValues extends FieldValues> = (
246
+ // fb: (f: QueryBuilder<TFieldValues>) => QueryBuilder<TFieldValues>
247
+ // ) => QueryBuilder<TFieldValues>
248
+
249
+ export interface QueryBuilder<TFieldValues extends FieldValues> {
250
+ type: "new-kid"
251
+ // TODO: as overloads?
252
+ and: FilterTest<TFieldValues>
253
+ or: FilterTest<TFieldValues>
254
+ build(): FilterResult[]
255
+ // where: <Filters extends Record<TFieldName extends FieldPath<TFieldValues>, V extends Value< FieldPathValue<TFieldValues, TFieldName>>>(filter: Filters) => QueryBuilder<TFieldValues>
256
+ }
@@ -0,0 +1,263 @@
1
+ import { codeFilter3 } from "./codeFilter.js"
2
+ import { buildWhereCosmosQuery3 } from "./Cosmos/query.js"
3
+ import { print, QueryBuilder } from "./filterApi/query.js"
4
+
5
+ const MyEntity = {
6
+ query: QueryBuilder
7
+ .make<MyEntity>()
8
+ }
9
+
10
+ const removeWhitespace = (a: string) => a.replaceAll(/\s+/g, " ").replaceAll(/^\s+/g, "").replaceAll(/\s+$/g, "")
11
+
12
+ interface MyEntity {
13
+ id: string
14
+ name: string
15
+ bio: string | null
16
+ isActive: boolean
17
+ state: { _tag: "a"; val: number } | { _tag: "b"; val: string } | { _tag: "c"; noVal: boolean }
18
+ roles: readonly string[]
19
+ tag: "a" | "b" | "c"
20
+ age?: number
21
+ something: {
22
+ id: number
23
+ name: string
24
+ } | null
25
+ }
26
+
27
+ describe("works", () => {
28
+ const f = MyEntity.query((where, f) =>
29
+ where(f.something.id.eq(1))
30
+ .and(() =>
31
+ where(f.something.name.startsWith("a")) // or would we do "like", "a%"?
32
+ .or(f.tag.in("a", "b"))
33
+ .or(() =>
34
+ where(f.name.neq("Alfredo"))
35
+ .and(f.tag("c"))
36
+ )
37
+ )
38
+ // .and(f.state._tag("a"))
39
+ .and("state._tag", "a")
40
+ .and(f.isActive(true))
41
+ .and(f.age.gte(12))
42
+ )
43
+
44
+ const s = f.build()
45
+ console.log(JSON.stringify(s, undefined, 2))
46
+ it("print", () =>
47
+ expect(print(s)).toBe(
48
+ `something.id eq 1 AND (
49
+ something.name startsWith a OR tag in a OR (
50
+ name neq Alfredo AND tag eq c
51
+ )
52
+ ) AND isActive eq true AND age gte 12`
53
+ ))
54
+ const values: MyEntity[] = [{
55
+ id: "1",
56
+ name: "Patrick",
57
+ bio: "something",
58
+ age: 13,
59
+ isActive: true,
60
+ state: { _tag: "a", val: 1 },
61
+ roles: ["admin"],
62
+ tag: "c",
63
+ something: { id: 1, name: "abc" }
64
+ }, {
65
+ id: "1",
66
+ name: "Patrick",
67
+ bio: "something",
68
+ age: 11,
69
+ isActive: true,
70
+ state: { _tag: "b", val: "2" },
71
+ roles: ["admin"],
72
+ tag: "a",
73
+ something: { id: 1, name: "abc" }
74
+ }]
75
+
76
+ it("print codef", () => {
77
+ expect(values.filter(codeFilter3(s))).toStrictEqual([values[0]])
78
+ })
79
+
80
+ it("codeFilter1", () => {
81
+ expect(values.filter(codeFilter3(s))).toStrictEqual([values[0]])
82
+ })
83
+ it("codeFilter2", () => {
84
+ expect(values.filter(
85
+ codeFilter3(
86
+ MyEntity
87
+ .query((where, f) =>
88
+ where(f.something.id.eq(0))
89
+ .and(() =>
90
+ where(f.something.name.startsWith("a"))
91
+ .or(f.tag.in("a", "b"))
92
+ )
93
+ )
94
+ .build()
95
+ )
96
+ ))
97
+ .toStrictEqual([])
98
+
99
+ expect(values.filter(
100
+ codeFilter3(
101
+ MyEntity
102
+ .query((where, f) => where(f.something.id.eq(0)))
103
+ .build()
104
+ )
105
+ ))
106
+ .toStrictEqual([])
107
+
108
+ expect(values.filter(
109
+ codeFilter3(
110
+ MyEntity
111
+ .query((where, f) => where(f.something.id.eq(1)))
112
+ .build()
113
+ )
114
+ ))
115
+ .toStrictEqual(values)
116
+
117
+ expect(values.filter(
118
+ codeFilter3(
119
+ MyEntity
120
+ .query((where, f) => where(f.age.eq(11)))
121
+ .build()
122
+ )
123
+ ))
124
+ .toStrictEqual([values[1]])
125
+ })
126
+
127
+ it("cosmos", () => {
128
+ const r = buildWhereCosmosQuery3(s, "MyEntity", "marker")
129
+ expect(removeWhitespace(r.query)).toBe(removeWhitespace(`
130
+ SELECT *
131
+ FROM MyEntity f
132
+ WHERE f.id != @id AND f.something.id = @v0 AND (
133
+ STARTSWITH(f.something.name, @v1, true) OR ARRAY_CONTAINS(@v2, f.tag) OR (
134
+ LOWER(f.name) <> LOWER(@v3) AND LOWER(f.tag) = LOWER(@v4)
135
+ )
136
+ ) AND f.isActive = @v5 AND f.age >= @v6`))
137
+ expect(r.parameters).toEqual([
138
+ {
139
+ "name": "@id",
140
+ "value": "marker"
141
+ },
142
+ {
143
+ "name": "@v0",
144
+ "value": 1
145
+ },
146
+ {
147
+ "name": "@v1",
148
+ "value": "a"
149
+ },
150
+ {
151
+ "name": "@v2",
152
+ "value": "a"
153
+ },
154
+ {
155
+ "name": "@v3",
156
+ "value": "Alfredo"
157
+ },
158
+ {
159
+ "name": "@v4",
160
+ "value": "c"
161
+ },
162
+ {
163
+ "name": "@v5",
164
+ "value": true
165
+ },
166
+ {
167
+ "name": "@v6",
168
+ "value": 12
169
+ }
170
+ ])
171
+ })
172
+ })
173
+
174
+ // ref https://stackoverflow.com/questions/1241142/sql-logic-operator-precedence-and-and-or
175
+ describe("root-or", () => {
176
+ const f = MyEntity.query((where, f) =>
177
+ where(() =>
178
+ where(f.something.id(1))
179
+ .and(() =>
180
+ where(f.something.name.startsWith("a")) // or would we do "like", "a%"?
181
+ .or(f.tag.in("a", "b"))
182
+ .or(() =>
183
+ where(f.name.neq("Alfredo"))
184
+ .and(f.tag("c"))
185
+ )
186
+ )
187
+ .and(f.bio.contains("abc"))
188
+ .and(f.isActive(true))
189
+ .and(f.age.gte(12))
190
+ )
191
+ .or(f.name.startsWith("C"))
192
+ )
193
+
194
+ const s = f.build()
195
+ console.log(JSON.stringify(s, undefined, 2))
196
+
197
+ it("print", () => {
198
+ expect(print(s)).toBe(
199
+ `(
200
+ something.id eq 1 AND (
201
+ something.name startsWith a OR tag in a OR (
202
+ name neq Alfredo AND tag eq c
203
+ )
204
+ ) AND bio contains abc AND isActive eq true AND age gte 12
205
+ ) OR name startsWith C`
206
+ )
207
+ })
208
+ it("cosmos", () => {
209
+ const r = buildWhereCosmosQuery3(s, "MyEntity", "marker")
210
+ expect(removeWhitespace(r.query)).toBe(removeWhitespace(` SELECT *
211
+ FROM MyEntity f
212
+
213
+ WHERE f.id != @id AND (
214
+ f.something.id = @v0 AND (
215
+ STARTSWITH(f.something.name, @v1, true) OR ARRAY_CONTAINS(@v2, f.tag) OR (
216
+ LOWER(f.name) <> LOWER(@v3) AND LOWER(f.tag) = LOWER(@v4)
217
+ )
218
+ ) AND CONTAINS(f.bio, @v5, true) AND f.isActive = @v6 AND f.age >= @v7
219
+ ) OR STARTSWITH(f.name, @v8, true) `))
220
+ expect(r.parameters).toEqual([
221
+ {
222
+ "name": "@id",
223
+ "value": "marker"
224
+ },
225
+ {
226
+ "name": "@v0",
227
+ "value": 1
228
+ },
229
+ {
230
+ "name": "@v1",
231
+ "value": "a"
232
+ },
233
+ {
234
+ "name": "@v2",
235
+ "value": "a"
236
+ },
237
+ {
238
+ "name": "@v3",
239
+ "value": "Alfredo"
240
+ },
241
+ {
242
+ "name": "@v4",
243
+ "value": "c"
244
+ },
245
+ {
246
+ "name": "@v5",
247
+ "value": "abc"
248
+ },
249
+ {
250
+ "name": "@v6",
251
+ "value": true
252
+ },
253
+ {
254
+ "name": "@v7",
255
+ "value": 12
256
+ },
257
+ {
258
+ "name": "@v8",
259
+ "value": "C"
260
+ }
261
+ ])
262
+ })
263
+ })
@@ -15,7 +15,6 @@ import { StoreMaker } from "./service.js"
15
15
  */
16
16
  export function StoreMakerLive(config: Config<StorageConfig>) {
17
17
  return config
18
- .config
19
18
  .flatMap((cfg) => {
20
19
  const storageUrl = cfg.url.value
21
20
  if (storageUrl.startsWith("mem://")) {
@@ -4,6 +4,8 @@ import type { Parser, ParserEnv } from "@effect-app/schema/custom/Parser"
4
4
 
5
5
  import type { These } from "@effect-app/prelude/schema"
6
6
  import type { OptimisticConcurrencyException } from "../../errors.js"
7
+ import type { FieldValues } from "../../filter/types.js"
8
+ import type { QueryBuilder } from "./filterApi/query.js"
7
9
 
8
10
  export type StoreConfig<E> = {
9
11
  uniqueKeys?: UniqueKey[]
@@ -50,10 +52,11 @@ export type JoinFindFilter = {
50
52
  valueKey: string /* value paths of E[keys][valueKey] */
51
53
  value: any /* value path[valueKey] of E */
52
54
  }
53
- export type Filter<E> =
55
+ export type Filter<E extends FieldValues> =
54
56
  | JoinFindFilter
55
57
  | StoreWhereFilter
56
58
  | LegacyFilter<E>
59
+ | QueryBuilder<E>
57
60
 
58
61
  export type FilterJoinSelect = {
59
62
  type: "filter_join_select"
@@ -62,12 +65,21 @@ export type FilterJoinSelect = {
62
65
  value: any /* value path[valueKey] of E */
63
66
  }
64
67
 
68
+ export interface FilterArgs<PM extends PersistenceModelType<string>, U extends keyof PM = never> {
69
+ filter?: Filter<PM>
70
+ select?: readonly U[]
71
+ limit?: number
72
+ skip?: number
73
+ }
74
+
75
+ export type FilterFunc<PM extends PersistenceModelType<string>> = <U extends keyof PM = never>(
76
+ args: FilterArgs<PM, U>
77
+ ) => Effect<never, never, (U extends undefined ? PM : Pick<PM, U>)[]>
78
+
65
79
  export interface Store<PM extends PersistenceModelType<Id>, Id extends string> {
66
80
  all: Effect<never, never, PM[]>
67
- filter: (
68
- filter: Filter<PM>,
69
- cursor?: { limit?: number; skip?: number }
70
- ) => Effect<never, never, PM[]>
81
+ filter: FilterFunc<PM>
82
+ /** @deprecated */
71
83
  filterJoinSelect: <T extends object>(
72
84
  filter: FilterJoinSelect
73
85
  ) => Effect<never, never, (T & { _rootId: string })[]>
@@ -5,6 +5,7 @@ import { OptimisticConcurrencyException } from "../../errors.js"
5
5
  import type { Filter, FilterJoinSelect, PersistenceModelType, SupportedValues2 } from "./service.js"
6
6
 
7
7
  import objectHash from "object-hash"
8
+ import { codeFilter3_ } from "./codeFilter.js"
8
9
 
9
10
  export const makeETag = <E extends PersistenceModelType<Id>, Id extends string>(
10
11
  { _etag, ...e }: E
@@ -38,7 +39,9 @@ export const makeUpdateETag =
38
39
 
39
40
  export function codeFilter<E extends { id: string }, NE extends E>(filter: Filter<NE>) {
40
41
  return (x: E) =>
41
- filter.type === "startsWith"
42
+ filter.type === "new-kid"
43
+ ? codeFilter3_(filter.build(), x) ? Option(x as unknown as NE) : Option.none
44
+ : filter.type === "startsWith"
42
45
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
46
  ? lowercaseIfString((x as any)[filter.by]).startsWith(filter.value.toLowerCase())
44
47
  ? Option(x as unknown as NE)
@@ -184,38 +187,38 @@ export function codeFilterJoinSelect<E extends { id: string }, NE>(
184
187
  .flatMap((_) => _)
185
188
  }
186
189
 
187
- function lowercaseIfString<T>(val: T) {
190
+ export function lowercaseIfString<T>(val: T) {
188
191
  if (typeof val === "string") {
189
192
  return val.toLowerCase()
190
193
  }
191
194
  return val
192
195
  }
193
196
 
194
- function compareCaseInsensitive(valA: unknown, valB: unknown) {
197
+ export function compareCaseInsensitive(valA: unknown, valB: unknown) {
195
198
  return typeof valB === "string" && typeof valA === "string"
196
199
  ? valA.toLowerCase() === valB.toLowerCase()
197
200
  : valA === valB
198
201
  }
199
202
 
200
- function ltCaseInsensitive(valA: SupportedValues2, valB: SupportedValues2) {
203
+ export function ltCaseInsensitive(valA: SupportedValues2, valB: SupportedValues2) {
201
204
  return typeof valB === "string" && typeof valA === "string"
202
205
  ? valA.toLowerCase() < valB.toLowerCase()
203
206
  : valA < valB
204
207
  }
205
208
 
206
- function lteCaseInsensitive(valA: SupportedValues2, valB: SupportedValues2) {
209
+ export function lteCaseInsensitive(valA: SupportedValues2, valB: SupportedValues2) {
207
210
  return typeof valB === "string" && typeof valA === "string"
208
211
  ? valA.toLowerCase() <= valB.toLowerCase()
209
212
  : valA <= valB
210
213
  }
211
214
 
212
- function gtCaseInsensitive(valA: SupportedValues2, valB: SupportedValues2) {
215
+ export function gtCaseInsensitive(valA: SupportedValues2, valB: SupportedValues2) {
213
216
  return typeof valB === "string" && typeof valA === "string"
214
217
  ? valA.toLowerCase() > valB.toLowerCase()
215
218
  : valA > valB
216
219
  }
217
220
 
218
- function gteCaseInsensitive(valA: SupportedValues2, valB: SupportedValues2) {
221
+ export function gteCaseInsensitive(valA: SupportedValues2, valB: SupportedValues2) {
219
222
  return typeof valB === "string" && typeof valA === "string"
220
223
  ? valA.toLowerCase() >= valB.toLowerCase()
221
224
  : valA >= valB
@@ -1 +1 @@
1
- {"version":3,"file":"Sendgrid.d.ts","sourceRoot":"","sources":["../../../_src/services/Emailer/Sendgrid.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4CAA4C,CAAA;AAG3E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAwB,cAAc,EAAE,MAAM,cAAc,CAAA;;;;;AAoDlF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC,0FAK1D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,OAAO,SAUpC,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMnB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,SAAS,WAO1C"}
1
+ {"version":3,"file":"Sendgrid.d.ts","sourceRoot":"","sources":["../../../_src/services/Emailer/Sendgrid.ts"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4CAA4C,CAAA;AAG3E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAwB,cAAc,EAAE,MAAM,cAAc,CAAA;;;;;AAoDlF,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC,0FAI1D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,SAAS,EAAE,OAAO,SAUpC,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMnB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,EAAE,EAAE,SAAS,WAO1C"}
@@ -42,7 +42,7 @@ const makeLiveSendgrid = ({ apiKey, defaultFrom, defaultReplyTo, realMail, subje
42
42
  * @tsplus static Emailer.Ops LiveSendgrid
43
43
  */
44
44
  export function LiveSendgrid(config) {
45
- return tsplus_module_3.effect(Emailer)(tsplus_module_2.flatMap(tsplus_module_2.config(config), makeLiveSendgrid));
45
+ return tsplus_module_3.effect(Emailer)(tsplus_module_2.flatMap(config, makeLiveSendgrid));
46
46
  }
47
47
  /**
48
48
  * @hidden
@@ -98,4 +98,4 @@ function renderMailData(md) {
98
98
  }
99
99
  return md.email;
100
100
  }
101
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VuZGdyaWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9fc3JjL3NlcnZpY2VzL0VtYWlsZXIvU2VuZGdyaWQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQ0EsT0FBTyxNQUFNLE1BQU0sZ0JBQWdCLENBQUE7QUFDbkMsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQTtBQUM5QixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBR3RDLE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsY0FBYyxFQUFFLFFBQVEsRUFBRSxhQUFhLEVBQWtCLEVBQUUsRUFBRSxDQUM1RyxxQkFBWSxHQUFHLEVBQUU7SUFDZixNQUFNLENBQUMsU0FBUyx1QkFBQyxNQUFNLEVBQU8sQ0FBQTtJQUU5QixPQUFPO1FBQ0wsUUFBUSxDQUFDLElBQTBCO1lBQ2pDLE9BQU8sb0JBQVcsUUFBUSxDQUFDLEVBQUMsQ0FBQztnQkFDM0IsTUFBTSxHQUFHLEdBQWE7b0JBQ3BCLEdBQUcsSUFBSTtvQkFDUCxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksSUFBSSxXQUFXO29CQUM5QixPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDO2lCQUNsRSxDQUFBO2dCQUNELE1BQU0sTUFBTSxHQUFHLGFBQWEsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFBO2dCQUV2QyxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUE7Z0JBQ2hDLE1BQU0sV0FBVyxHQUFHLEVBQUUsR0FBRyxZQUFZLEVBQUUsT0FBTyxFQUFFLEdBQUcsYUFBYSxHQUFHLFlBQVksQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFBO2dCQUMzRixLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsNkJBQUEseUJBQWdCLGVBQWUsQ0FBQyxFQUFjLEtBQUssRUFBRSxPQUFPLENBQUMsV0FBVyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUE7Z0JBRTlGLE1BQU0sR0FBRyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FDbEIsc0JBS0UsQ0FBQyxFQUFFLEVBQUUsRUFBRSxDQUNMLEtBQUssTUFBTSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUUsS0FBSyxFQUFFLENBQUMsR0FBRyxFQUFFLE1BQU0sRUFBRSxFQUFFLENBQ25ELEdBQUc7b0JBQ0QsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxxQkFBWSxHQUFHLENBQUMsQ0FBQztvQkFDdEIsQ0FBQyxDQUFDLEVBQUUsQ0FBQywyQkFBTyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQzVCLENBQ0YsQ0FBQTtnQkFFRCxrQkFBa0I7Z0JBQ2xCLHVCQUF1QjtnQkFDdkIsa0JBQWtCO2dCQUNsQixpQ0FBaUM7Z0JBQ2pDLE1BQU07Z0JBQ04sSUFBSTtnQkFDSiwyRkFBMkY7Z0JBQzNGLG9EQUFvRDtnQkFDcEQsb0JBQW9CO2dCQUNwQixPQUFPLEdBQUcsQ0FBQTtZQUNaLENBQUMsQ0FBQyxDQUFBO1FBQ0osQ0FBQztLQUNGLENBQUE7QUFDSCxDQUFDLENBQUMsQ0FBQTtBQUVKOztHQUVHO0FBQ0gsTUFBTSxVQUFVLFlBQVksQ0FBQyxNQUE4QjtJQUN6RCxPQUFPLHVCQUdJLE9BQU8sRUFIWCwrQ0FBQSxNQUFNLEdBRUYsZ0JBQWdCLENBQUMsQ0FDVCxDQUFBO0FBQ3JCLENBQUM7QUFFRDs7R0FFRztBQUNILE1BQU0sVUFBVSxhQUFhLENBQUMsU0FBa0I7SUFDOUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFBO0lBQ1QsTUFBTSxNQUFNLEdBQUcsR0FBRyxFQUFFLENBQUMsQ0FBQyxFQUFFLENBQUE7SUFDeEIsT0FBTyxTQUFTO1FBQ2QsQ0FBQyxDQUFDLENBQUMsR0FBYSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQ3BCLEdBQUcsR0FBRztZQUNOLEVBQUUsRUFBRSxHQUFHLENBQUMsRUFBRSxJQUFJLFVBQVUsQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLE1BQU0sQ0FBQztZQUN4QyxFQUFFLEVBQUUsR0FBRyxDQUFDLEVBQUUsSUFBSSxVQUFVLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxNQUFNLENBQUM7WUFDeEMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxHQUFHLElBQUksVUFBVSxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsTUFBTSxDQUFDO1NBQzVDLENBQUM7UUFDRixDQUFDLENBQUMsQ0FBQyxHQUFhLEVBQUUsRUFBRSxDQUFDLENBQUM7WUFDcEIsR0FBRyxHQUFHO1lBQ04sRUFBRSxFQUFFLEdBQUcsQ0FBQyxFQUFFLElBQUksZ0JBQWdCLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxNQUFNLENBQUM7WUFDOUMsRUFBRSxFQUFFLEdBQUcsQ0FBQyxFQUFFLElBQUksZ0JBQWdCLENBQUMsR0FBRyxDQUFDLEVBQUUsRUFBRSxNQUFNLENBQUM7WUFDOUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxHQUFHLElBQUksZ0JBQWdCLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxNQUFNLENBQUM7U0FDbEQsQ0FBQyxDQUFBO0FBQ04sQ0FBQztBQUVEOztHQUVHO0FBQ0gsTUFBTSxVQUFVLGFBQWEsQ0FBQyxFQUFhO0lBQ3pDLE9BQU8sQ0FDTCxDQUFDLE9BQU8sRUFBRSxLQUFLLFFBQVEsSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1dBQzNELENBQUMsT0FBTyxFQUFFLEtBQUssUUFBUTtlQUNyQixPQUFPLElBQUksRUFBRTtlQUNiLEVBQUUsQ0FBQyxLQUFLLENBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQy9DLENBQUE7QUFDSCxDQUFDO0FBRUQsU0FBUyxVQUFVLENBQUMsSUFBNkIsRUFBRSxNQUFvQjtJQUNyRSxPQUFPO1FBQ0wsSUFBSSxFQUFFLGNBQWMsQ0FBQyxJQUFJLENBQUM7UUFDMUIsS0FBSyxFQUFFLFFBQVEsTUFBTSxFQUFFLGFBQWE7S0FDckMsQ0FBQTtBQUNILENBQUM7QUFDRCxNQUFNLEVBQUUsR0FBRyxpREFBNEIsQ0FBQyxFQUE2QyxFQUFFLEVBQUUsQ0FDdkYsT0FBTyxFQUFFLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsV0FBVyxFQUFFLENBQ25FLENBQUE7QUFFRCw0REFBNEQ7QUFDNUQsZ0RBQWdEO0FBQ2hELFNBQVMsZ0JBQWdCLENBQUMsSUFBNkIsRUFBRSxNQUFvQjtJQUMzRSxPQUFPLEtBQUssQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDO1FBQ3hCLENBQUMsaUNBQUMsMEJBRU0sRUFBRSxFQUZSLElBQUk7YUFDSCxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVLENBQUMsQ0FBQyxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUNsRCxFQUVYLENBQUMsQ0FBQyxhQUFhLENBQUMsSUFBSSxDQUFDO1FBQ3JCLENBQUMsQ0FBQyxVQUFVLENBQUMsSUFBSSxFQUFFLE1BQU0sQ0FBQztRQUMxQixDQUFDLENBQUMsSUFBSSxDQUFBO0FBQ1YsQ0FBQztBQUVELFNBQVMsY0FBYyxDQUFDLEVBQStCO0lBQ3JELElBQUksS0FBSyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDO1FBQ3RCLE9BQU8sRUFBRSxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUE7SUFDMUMsQ0FBQztJQUNELElBQUksT0FBTyxFQUFFLEtBQUssUUFBUSxFQUFFLENBQUM7UUFDM0IsT0FBTyxFQUFFLENBQUE7SUFDWCxDQUFDO0lBQ0QsT0FBTyxFQUFFLENBQUMsS0FBSyxDQUFBO0FBQ2pCLENBQUMifQ==
101
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU2VuZGdyaWQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9fc3JjL3NlcnZpY2VzL0VtYWlsZXIvU2VuZGdyaWQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQ0EsT0FBTyxNQUFNLE1BQU0sZ0JBQWdCLENBQUE7QUFDbkMsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQTtBQUM5QixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sY0FBYyxDQUFBO0FBR3RDLE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxFQUFFLE1BQU0sRUFBRSxXQUFXLEVBQUUsY0FBYyxFQUFFLFFBQVEsRUFBRSxhQUFhLEVBQWtCLEVBQUUsRUFBRSxDQUM1RyxxQkFBWSxHQUFHLEVBQUU7SUFDZixNQUFNLENBQUMsU0FBUyx1QkFBQyxNQUFNLEVBQU8sQ0FBQTtJQUU5QixPQUFPO1FBQ0wsUUFBUSxDQUFDLElBQTBCO1lBQ2pDLE9BQU8sb0JBQVcsUUFBUSxDQUFDLEVBQUMsQ0FBQztnQkFDM0IsTUFBTSxHQUFHLEdBQWE7b0JBQ3BCLEdBQUcsSUFBSTtvQkFDUCxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksSUFBSSxXQUFXO29CQUM5QixPQUFPLEVBQUUsSUFBSSxDQUFDLE9BQU8sSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDO2lCQUNsRSxDQUFBO2dCQUNELE1BQU0sTUFBTSxHQUFHLGFBQWEsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFBO2dCQUV2QyxNQUFNLFlBQVksR0FBRyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUE7Z0JBQ2hDLE1BQU0sV0FBVyxHQUFHLEVBQUUsR0FBRyxZQUFZLEVBQUUsT0FBTyxFQUFFLEdBQUcsYUFBYSxHQUFHLFlBQVksQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFBO2dCQUMzRixLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsNkJB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