feathers-utils 2.0.0-2 → 2.0.0-4

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 (139) hide show
  1. package/dist/esm/filters/array.d.ts +1 -1
  2. package/dist/esm/filters/array.js +2 -2
  3. package/dist/esm/filters/object.d.ts +2 -0
  4. package/dist/esm/filters/object.js +8 -0
  5. package/dist/esm/hooks/checkMulti.d.ts +2 -2
  6. package/dist/esm/hooks/createRelated.d.ts +8 -2
  7. package/dist/esm/hooks/createRelated.js +2 -3
  8. package/dist/esm/hooks/onDelete.d.ts +8 -2
  9. package/dist/esm/hooks/onDelete.js +8 -7
  10. package/dist/esm/hooks/removeRelated.d.ts +7 -2
  11. package/dist/esm/hooks/removeRelated.js +8 -7
  12. package/dist/esm/hooks/runPerItem.d.ts +5 -2
  13. package/dist/esm/hooks/runPerItem.js +1 -1
  14. package/dist/esm/hooks/setData.d.ts +7 -2
  15. package/dist/esm/hooks/setData.js +3 -2
  16. package/dist/esm/index.d.ts +19 -34
  17. package/dist/esm/index.js +21 -33
  18. package/dist/esm/mixins/debounce-mixin/DebouncedStore.d.ts +5 -2
  19. package/dist/esm/mixins/debounce-mixin/DebouncedStore.js +2 -2
  20. package/dist/esm/mixins/debounce-mixin/debounceMixin.d.ts +3 -0
  21. package/dist/esm/mixins/debounce-mixin/debounceMixin.js +19 -0
  22. package/dist/esm/mixins/debounce-mixin/index.d.ts +3 -8
  23. package/dist/esm/mixins/debounce-mixin/index.js +3 -20
  24. package/dist/esm/mixins/debounce-mixin/types.d.ts +13 -0
  25. package/dist/esm/mixins/debounce-mixin/types.js +1 -0
  26. package/dist/esm/types.d.ts +1 -77
  27. package/dist/esm/types.js +0 -1
  28. package/dist/esm/typesInternal.d.ts +3 -0
  29. package/dist/esm/typesInternal.js +3 -0
  30. package/dist/esm/utils/filterQuery.d.ts +6 -1
  31. package/dist/esm/utils/filterQuery.js +5 -3
  32. package/dist/esm/utils/getItemsIsArray.d.ts +5 -2
  33. package/dist/esm/utils/getItemsIsArray.js +7 -12
  34. package/dist/esm/utils/getPaginate.d.ts +2 -6
  35. package/dist/esm/utils/getPaginate.js +1 -1
  36. package/dist/esm/utils/isMulti.d.ts +1 -1
  37. package/dist/esm/utils/isPaginated.d.ts +1 -1
  38. package/dist/esm/utils/markHookForSkip.d.ts +3 -2
  39. package/dist/esm/utils/markHookForSkip.js +6 -5
  40. package/dist/esm/utils/mergeQuery/index.d.ts +3 -3
  41. package/dist/esm/utils/mergeQuery/index.js +3 -338
  42. package/dist/esm/utils/mergeQuery/mergeArrays.d.ts +2 -1
  43. package/dist/esm/utils/mergeQuery/mergeArrays.js +2 -2
  44. package/dist/esm/utils/mergeQuery/mergeQuery.d.ts +3 -0
  45. package/dist/esm/utils/mergeQuery/mergeQuery.js +68 -0
  46. package/dist/esm/utils/mergeQuery/types.d.ts +13 -0
  47. package/dist/esm/utils/mergeQuery/types.js +1 -0
  48. package/dist/esm/utils/mergeQuery/utils.d.ts +11 -0
  49. package/dist/esm/utils/mergeQuery/utils.js +272 -0
  50. package/dist/esm/utils/pushSet.d.ts +4 -1
  51. package/dist/esm/utils/pushSet.js +1 -1
  52. package/dist/esm/utils/setResultEmpty.d.ts +1 -1
  53. package/dist/esm/utils/setResultEmpty.js +1 -1
  54. package/dist/esm/utils/shouldSkip.d.ts +1 -1
  55. package/dist/esm/utils/validateQueryProperty.js +1 -1
  56. package/dist/filters/array.d.ts +1 -1
  57. package/dist/filters/array.js +2 -2
  58. package/dist/filters/object.d.ts +2 -0
  59. package/dist/filters/object.js +15 -0
  60. package/dist/hooks/checkMulti.d.ts +2 -2
  61. package/dist/hooks/createRelated.d.ts +8 -2
  62. package/dist/hooks/createRelated.js +2 -3
  63. package/dist/hooks/onDelete.d.ts +8 -2
  64. package/dist/hooks/onDelete.js +8 -7
  65. package/dist/hooks/removeRelated.d.ts +7 -2
  66. package/dist/hooks/removeRelated.js +8 -7
  67. package/dist/hooks/runPerItem.d.ts +5 -2
  68. package/dist/hooks/runPerItem.js +1 -1
  69. package/dist/hooks/setData.d.ts +7 -2
  70. package/dist/hooks/setData.js +3 -2
  71. package/dist/index.d.ts +19 -34
  72. package/dist/index.js +23 -49
  73. package/dist/mixins/debounce-mixin/DebouncedStore.d.ts +5 -2
  74. package/dist/mixins/debounce-mixin/DebouncedStore.js +2 -2
  75. package/dist/mixins/debounce-mixin/debounceMixin.d.ts +3 -0
  76. package/dist/mixins/debounce-mixin/debounceMixin.js +23 -0
  77. package/dist/mixins/debounce-mixin/index.d.ts +3 -8
  78. package/dist/mixins/debounce-mixin/index.js +17 -22
  79. package/dist/mixins/debounce-mixin/types.d.ts +13 -0
  80. package/dist/mixins/debounce-mixin/types.js +2 -0
  81. package/dist/types.d.ts +1 -77
  82. package/dist/types.js +0 -1
  83. package/dist/typesInternal.d.ts +3 -0
  84. package/dist/typesInternal.js +4 -0
  85. package/dist/utils/filterQuery.d.ts +6 -1
  86. package/dist/utils/filterQuery.js +4 -2
  87. package/dist/utils/getItemsIsArray.d.ts +5 -2
  88. package/dist/utils/getItemsIsArray.js +7 -12
  89. package/dist/utils/getPaginate.d.ts +2 -6
  90. package/dist/utils/isMulti.d.ts +1 -1
  91. package/dist/utils/isPaginated.d.ts +1 -1
  92. package/dist/utils/markHookForSkip.d.ts +3 -2
  93. package/dist/utils/markHookForSkip.js +6 -5
  94. package/dist/utils/mergeQuery/index.d.ts +3 -3
  95. package/dist/utils/mergeQuery/index.js +16 -342
  96. package/dist/utils/mergeQuery/mergeArrays.d.ts +2 -1
  97. package/dist/utils/mergeQuery/mergeArrays.js +2 -2
  98. package/dist/utils/mergeQuery/mergeQuery.d.ts +3 -0
  99. package/dist/utils/mergeQuery/mergeQuery.js +75 -0
  100. package/dist/utils/mergeQuery/types.d.ts +13 -0
  101. package/dist/utils/mergeQuery/types.js +2 -0
  102. package/dist/utils/mergeQuery/utils.d.ts +11 -0
  103. package/dist/utils/mergeQuery/utils.js +287 -0
  104. package/dist/utils/pushSet.d.ts +4 -1
  105. package/dist/utils/pushSet.js +1 -1
  106. package/dist/utils/setResultEmpty.d.ts +1 -1
  107. package/dist/utils/setResultEmpty.js +1 -1
  108. package/dist/utils/shouldSkip.d.ts +1 -1
  109. package/package.json +5 -2
  110. package/src/filters/array.ts +13 -9
  111. package/src/filters/object.ts +15 -0
  112. package/src/hooks/checkMulti.ts +8 -6
  113. package/src/hooks/createRelated.ts +21 -12
  114. package/src/hooks/onDelete.ts +28 -13
  115. package/src/hooks/removeRelated.ts +28 -16
  116. package/src/hooks/runPerItem.ts +19 -10
  117. package/src/hooks/setData.ts +24 -15
  118. package/src/index.ts +21 -38
  119. package/src/mixins/debounce-mixin/DebouncedStore.ts +29 -24
  120. package/src/mixins/debounce-mixin/debounceMixin.ts +33 -0
  121. package/src/mixins/debounce-mixin/index.ts +3 -39
  122. package/src/mixins/debounce-mixin/types.ts +16 -0
  123. package/src/types.ts +6 -117
  124. package/src/typesInternal.ts +6 -0
  125. package/src/utils/filterQuery.ts +22 -10
  126. package/src/utils/getItemsIsArray.ts +15 -16
  127. package/src/utils/getPaginate.ts +11 -14
  128. package/src/utils/isMulti.ts +3 -3
  129. package/src/utils/isPaginated.ts +6 -4
  130. package/src/utils/markHookForSkip.ts +18 -16
  131. package/src/utils/mergeQuery/index.ts +3 -379
  132. package/src/utils/mergeQuery/mergeArrays.ts +25 -18
  133. package/src/utils/mergeQuery/mergeQuery.ts +102 -0
  134. package/src/utils/mergeQuery/types.ts +25 -0
  135. package/src/utils/mergeQuery/utils.ts +342 -0
  136. package/src/utils/pushSet.ts +14 -7
  137. package/src/utils/setResultEmpty.ts +8 -6
  138. package/src/utils/shouldSkip.ts +4 -4
  139. package/src/utils/validateQueryProperty.ts +8 -4
@@ -1,12 +1,14 @@
1
1
  import type { HookContext } from "@feathersjs/feathers";
2
2
  import { getPaginate } from "./getPaginate";
3
3
 
4
- export const isPaginated = (
5
- context: HookContext
4
+ export const isPaginated = <H extends HookContext = HookContext>(
5
+ context: H
6
6
  ): boolean => {
7
- if (context.params.paginate === false) { return false; }
7
+ if (context.params.paginate === false) {
8
+ return false;
9
+ }
8
10
 
9
11
  const paginate = getPaginate(context);
10
12
 
11
13
  return !!paginate;
12
- };
14
+ };
@@ -1,24 +1,26 @@
1
1
  import { pushSet } from "./pushSet";
2
2
 
3
3
  import type { HookContext } from "@feathersjs/feathers";
4
- import type { HookType, MaybeArray } from "../types";
4
+ import type { HookType } from "feathers-hooks-common";
5
+ import type { MaybeArray } from "../typesInternal";
5
6
 
6
- export function markHookForSkip<T>(
7
- hookName: string,
8
- type: "all" | MaybeArray<HookType>,
9
- context?: Partial<HookContext<T>>
10
- ): Partial<HookContext<T>> {
7
+ export function markHookForSkip<H extends HookContext = HookContext>(
8
+ hookName: string,
9
+ type: "all" | MaybeArray<HookType>,
10
+ context?: H
11
+ ) {
12
+ // @ts-expect-error context is not of type 'H'
11
13
  context = context || {};
12
- const params = context.params || {};
13
- const types: string[] = (Array.isArray(type)) ? type : [type];
14
-
15
- types.forEach(t => {
16
- const combinedName = (t === "all")
17
- ? hookName
18
- : `${type}:${hookName}`;
14
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
15
+ const params = context!.params || {};
16
+ const types: string[] = Array.isArray(type) ? type : [type];
17
+
18
+ types.forEach((t) => {
19
+ const combinedName = t === "all" ? hookName : `${type}:${hookName}`;
19
20
  pushSet(params, ["skipHooks"], combinedName, { unique: true });
20
21
  });
21
-
22
- context.params = params;
22
+
23
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
24
+ context!.params = params;
23
25
  return context;
24
- }
26
+ }
@@ -1,379 +1,3 @@
1
- import _get from "lodash/get.js";
2
- import _has from "lodash/has.js";
3
- import _isEmpty from "lodash/isEmpty.js";
4
- import _isEqual from "lodash/isEqual.js";
5
- import _merge from "lodash/merge.js";
6
- import _set from "lodash/set.js";
7
- import _uniqWith from "lodash/uniqWith.js";
8
-
9
- import { mergeArrays } from "./mergeArrays";
10
- import { filterQuery } from "../filterQuery";
11
-
12
- import { Forbidden } from "@feathersjs/errors";
13
-
14
- import type { Query } from "@feathersjs/feathers";
15
-
16
- import type {
17
- Handle,
18
- MergeQueryOptions,
19
- Path
20
- } from "../../types";
21
-
22
- const hasOwnProperty = (obj: Record<string, unknown>, key: string): boolean => {
23
- return Object.prototype.hasOwnProperty.call(obj, key);
24
- };
25
-
26
- function handleArray<T>(target: Record<string, unknown>, source: Record<string, unknown>, key: Path, options: MergeQueryOptions<T>): void {
27
- const targetVal = _get(target, key);
28
- const sourceVal = _get(source, key);
29
- if (!sourceVal && !targetVal) { return; }
30
- const handle: Handle = _get(options, ["handle", ...key], options.defaultHandle);
31
- const arr = mergeArrays(targetVal, sourceVal, handle, key, options.actionOnEmptyIntersect);
32
- _set(target, key, arr);
33
- }
34
-
35
- function handleCircular<T>(target: Record<string, unknown>, source: Record<string, unknown>, prependKey: Path, options: MergeQueryOptions<T>): void {
36
- if (target?.$or) {
37
- target.$or = cleanOr(target.$or as Record<string, unknown>[]);
38
- if (!target.$or) { delete target.$or; }
39
- }
40
- if (source?.$or) {
41
- source.$or = cleanOr(source.$or as Record<string, unknown>[]);
42
- if (!source.$or) { delete source.$or; }
43
- }
44
-
45
- if (target?.$and) {
46
- target.$and = cleanAnd(target.$and as Record<string, unknown>[]);
47
- if (!target.$and) { delete target.$and; }
48
- }
49
-
50
- if (source?.$and) {
51
- source.$and = cleanAnd(source.$and as Record<string, unknown>[]);
52
- if (!source.$and) { delete source.$and; }
53
- }
54
-
55
- if (!_has(source, prependKey)) { return; }
56
-
57
- if (!_has(target, prependKey)) {
58
- _set(target, prependKey, _get(source, prependKey));
59
- return;
60
- }
61
-
62
- const {
63
- defaultHandle,
64
- actionOnEmptyIntersect
65
- } = options;
66
-
67
- if (defaultHandle === "target") { return; }
68
-
69
-
70
- const getTargetVal = () => {
71
- return (prependKey.length > 0) ? _get(target, prependKey) : target;
72
- };
73
-
74
- const getSourceVal = () => {
75
- return (prependKey.length > 0) ? _get(source, prependKey) : source;
76
- };
77
-
78
- const targetVal = getTargetVal();
79
- const sourceVal = getSourceVal();
80
-
81
- if (_isEqual(targetVal, sourceVal)) {
82
- return;
83
- }
84
-
85
- if (defaultHandle === "source") {
86
- _set(target, prependKey, sourceVal);
87
- return;
88
- }
89
-
90
- if (targetVal === null || sourceVal === null) {
91
- _set(target, prependKey, sourceVal);
92
- return;
93
- }
94
-
95
- const typeOfTargetVal = typeof targetVal;
96
-
97
- if (["boolean"].includes(typeOfTargetVal)) {
98
- if (defaultHandle === "intersect") {
99
- actionOnEmptyIntersect(target, source, prependKey);
100
- }
101
- _set(target, prependKey, sourceVal);
102
- return;
103
- }
104
-
105
- const typeOfSourceVal = typeof sourceVal;
106
-
107
- const isTargetSimple = ["string", "number"].includes(typeOfTargetVal);
108
- const isSourceSimple = ["string", "number"].includes(typeOfSourceVal);
109
-
110
- if (isTargetSimple || isSourceSimple) {
111
- if (isTargetSimple && isSourceSimple) {
112
- if (defaultHandle === "combine") {
113
- _set(target, prependKey, { $in: [...new Set([targetVal, sourceVal])] });
114
- return;
115
- } else if (defaultHandle === "intersect") {
116
- actionOnEmptyIntersect(target, source, prependKey);
117
- } else {
118
- throw new Error("should not reach here");
119
- }
120
- } else if (hasOwnProperty(targetVal, "$in") || hasOwnProperty(sourceVal, "$in")) {
121
- const targetHasIn = hasOwnProperty(targetVal, "$in");
122
-
123
- const $in = (targetHasIn) ? targetVal["$in"] : sourceVal["$in"];
124
- const otherVal = (isTargetSimple) ? targetVal : sourceVal;
125
- if ($in.length === 1 && _isEqual($in[0], otherVal)) {
126
- _set(target, prependKey, otherVal);
127
- return;
128
- } else if (defaultHandle === "combine") {
129
- if (!$in.some((x: unknown) => _isEqual(x, otherVal))) {
130
- $in.push(otherVal);
131
- }
132
- _set(target, `${prependKey}.$in`, $in);
133
- return;
134
- } else if (defaultHandle === "intersect") {
135
- if ($in.some((x: unknown) => _isEqual(x, otherVal))) {
136
- _set(target, prependKey, otherVal);
137
- } else {
138
- actionOnEmptyIntersect(target, source, prependKey);
139
- }
140
- return;
141
- }
142
- return;
143
- }
144
- }
145
-
146
- const isTargetArray = Array.isArray(targetVal);
147
- const isSourceArray = Array.isArray(sourceVal);
148
-
149
- if (isTargetArray && isSourceArray) {
150
- const key = prependKey[prependKey.length-1];
151
- if (key === "$or") {
152
- if (defaultHandle === "combine") {
153
- const newVals = sourceVal.filter((x: unknown) => !targetVal.some((y: unknown) => _isEqual(x, y)));
154
- targetVal.push(...newVals);
155
- } else if (defaultHandle === "intersect") {
156
- // combine into "$and"
157
- const targetParent = getParentProp(target, prependKey);
158
- const sourceParent = getParentProp(source, prependKey);
159
- targetParent.$and = targetParent.$and || [];
160
-
161
- targetParent.$and.push(
162
- { $or: targetVal },
163
- { $or: sourceVal }
164
- );
165
-
166
- targetParent.$and = cleanAnd(targetParent.$and);
167
- if (!targetParent.$and) { delete targetParent.$and; }
168
- delete targetParent.$or;
169
- delete sourceParent.$or;
170
- handleCircular(target, source, [...prependKey, "$and"], options);
171
- return;
172
- }
173
- return;
174
- } else if (key === "$and") {
175
- if (defaultHandle === "combine") {
176
- // combine into "$or"
177
- const targetParent = getParentProp(target, prependKey);
178
- const sourceParent = getParentProp(source, prependKey);
179
-
180
- targetParent.$or = targetParent.$or || [];
181
- targetParent.$or.push(
182
- { $and: targetVal },
183
- { $and: sourceVal }
184
- );
185
- targetParent.$or = cleanOr(targetParent.$or);
186
- if (!targetParent.$or) { delete targetParent.$or; }
187
- delete targetParent.$and;
188
- delete sourceParent.$and;
189
- handleCircular(target, source, [...prependKey, "$or"], options);
190
- return;
191
- } else if (defaultHandle === "intersect") {
192
- const newVals = sourceVal.filter((x: unknown) => !targetVal.some((y: unknown) => _isEqual(x, y)));
193
- targetVal.push(...newVals);
194
- return;
195
- }
196
- } else if (key === "$in") {
197
- if (defaultHandle === "combine") {
198
- let $in: unknown[] = targetVal.concat(sourceVal);
199
- $in = [...new Set($in)];
200
- _set(target, prependKey, $in);
201
- return;
202
- } else if (defaultHandle === "intersect") {
203
- const $in = targetVal.filter((x: unknown) => sourceVal.some((y: unknown) => _isEqual(x, y)));
204
- if ($in.length === 0) {
205
- actionOnEmptyIntersect(target, source, prependKey);
206
-
207
- } else if ($in.length === 1) {
208
- _set(target, prependKey.slice(0, -1), $in[0]);
209
- return;
210
- } else {
211
- _set(target, prependKey, $in);
212
- }
213
- }
214
- return;
215
- }
216
-
217
- _set(target, prependKey, sourceVal);
218
- return;
219
- }
220
-
221
- if (typeOfTargetVal !== "object" || typeOfSourceVal !== "object") {
222
- _set(target, prependKey, sourceVal);
223
- return;
224
- }
225
-
226
- // both are objects
227
- const sourceKeys = Object.keys(sourceVal);
228
-
229
- for (let i = 0, n = sourceKeys.length; i < n; i++) {
230
- const key = sourceKeys[i];
231
- handleCircular(target, source, [...prependKey, key], options);
232
- }
233
- }
234
-
235
- function makeDefaultOptions<T>(options?: Partial<MergeQueryOptions<T>>): MergeQueryOptions<T> {
236
- options = options || {} as MergeQueryOptions<T>;
237
- options.defaultHandle = options.defaultHandle || "combine";
238
- options.useLogicalConjunction =
239
- (Object.prototype.hasOwnProperty.call(options, "useLogicalConjunction"))
240
- ? options.useLogicalConjunction
241
- : false;
242
- options.actionOnEmptyIntersect = options.actionOnEmptyIntersect || (() => {
243
- throw new Forbidden("You're not allowed to make this request");
244
- });
245
- options.handle = options.handle || {};
246
- if (options.defaultHandle === "intersect") {
247
- options.handle.$select = options.handle.$select || "intersectOrFull";
248
- }
249
- return options as MergeQueryOptions<T>;
250
- }
251
-
252
- function moveProperty(source: Record<string, any>, target: Record<string, any>, key: string): void {
253
- if (!Object.prototype.hasOwnProperty.call(source, key)) { return; }
254
- target[key] = source[key];
255
- delete source[key];
256
- }
257
-
258
- export function mergeQuery<T>(target: Query, source: Query, options?: Partial<MergeQueryOptions<T>>): Query {
259
- const fullOptions = makeDefaultOptions(options);
260
- const {
261
- filters: targetFilters,
262
- query: targetQuery
263
- } = filterQuery(target, {
264
- operators: fullOptions.operators,
265
- service: fullOptions.service
266
- });
267
-
268
- moveProperty(targetFilters, targetQuery, "$or");
269
- moveProperty(targetFilters, targetQuery, "$and");
270
-
271
- if (target.$limit) { targetFilters.$limit = target.$limit; }
272
-
273
- let {
274
- // eslint-disable-next-line prefer-const
275
- filters: sourceFilters,
276
- query: sourceQuery
277
- } = filterQuery(source, {
278
- operators: fullOptions.operators,
279
- service: fullOptions.service
280
- });
281
-
282
- moveProperty(sourceFilters, sourceQuery, "$or");
283
- moveProperty(sourceFilters, sourceQuery, "$and");
284
-
285
- if (source.$limit) { sourceFilters.$limit = source.$limit; }
286
-
287
- //#region filters
288
-
289
- if (
290
- target &&
291
- !Object.prototype.hasOwnProperty.call(target, "$limit") &&
292
- Object.prototype.hasOwnProperty.call(targetFilters, "$limit")
293
- ) {
294
- delete targetFilters.$limit;
295
- }
296
-
297
- if (
298
- source &&
299
- !Object.prototype.hasOwnProperty.call(source, "$limit") &&
300
- Object.prototype.hasOwnProperty.call(sourceFilters, "$limit")
301
- ) {
302
- delete sourceFilters.$limit;
303
- }
304
-
305
- handleArray(targetFilters, sourceFilters, ["$select"], fullOptions);
306
- // remaining filters
307
- delete sourceFilters["$select"];
308
- _merge(targetFilters, sourceFilters);
309
-
310
- //#endregion
311
-
312
- //#region '$or' / '$and'
313
-
314
- if (
315
- options?.useLogicalConjunction &&
316
- (
317
- options.defaultHandle === "combine" ||
318
- options.defaultHandle === "intersect"
319
- ) &&
320
- !_isEmpty(targetQuery)
321
- ) {
322
- const logicalOp =
323
- (options.defaultHandle === "combine")
324
- ? "$or"
325
- : "$and";
326
- if (Object.prototype.hasOwnProperty.call(sourceQuery, logicalOp)) {
327
- // omit '$or'/'$and' and put all other props into '$or'/'$and'
328
- const andOr = sourceQuery[logicalOp] as unknown[];
329
- delete sourceQuery[logicalOp];
330
- andOr.push(sourceQuery);
331
- sourceQuery = { [logicalOp]: andOr };
332
- } else {
333
- sourceQuery = { [logicalOp]: [sourceQuery] };
334
- }
335
- }
336
-
337
- //#endregion
338
-
339
- const keys = Object.keys(sourceQuery);
340
- for (let i = 0, n = keys.length; i < n; i++) {
341
- const key = keys[i];
342
- handleCircular(targetQuery, sourceQuery, [key], fullOptions);
343
- }
344
- const result = Object.assign({}, targetFilters, targetQuery) as Query;
345
-
346
- return result;
347
- }
348
-
349
- function getParentProp(target: Record<string, unknown>, path: Path) {
350
- if (path.length <= 1) { return target; }
351
- const pathOneUp = path.slice(0, -1);
352
- return _get(target, pathOneUp);
353
- }
354
-
355
- function cleanOr(target: Record<string, unknown>[]): Record<string, unknown>[] | undefined {
356
- if (!target || !Array.isArray(target) || target.length <= 0) { return target; }
357
-
358
- if (target.some(x => _isEmpty(x))) {
359
- return undefined;
360
- } else {
361
- return arrayWithoutDuplicates(target);
362
- }
363
- }
364
-
365
- function cleanAnd(target: Record<string, unknown>[]): Record<string, unknown>[] | undefined {
366
- if (!target || !Array.isArray(target) || target.length <= 0) { return target; }
367
-
368
- if (target.every(x => _isEmpty(x))) {
369
- return undefined;
370
- } else {
371
- target = target.filter(x => !_isEmpty(x));
372
- return arrayWithoutDuplicates(target);
373
- }
374
- }
375
-
376
- function arrayWithoutDuplicates<T>(target: T[]): T[] {
377
- if (!target || !Array.isArray(target)) { return target; }
378
- return _uniqWith(target, _isEqual);
379
- }
1
+ export * from "./mergeArrays";
2
+ export * from "./mergeQuery";
3
+ export * from "./types";
@@ -1,24 +1,27 @@
1
- import type {
2
- Handle,
3
- ActionOnEmptyIntersect,
4
- Path
5
- } from "../../types";
1
+ import type { Path } from "../../typesInternal";
2
+ import type { Handle, ActionOnEmptyIntersect } from "./types";
6
3
 
7
- export function mergeArrays<T> (
8
- targetArr: T[],
9
- sourceArr: T[],
10
- handle: Handle,
11
- prependKey?: Path,
4
+ export function mergeArrays<T>(
5
+ targetArr: T[],
6
+ sourceArr: T[],
7
+ handle: Handle,
8
+ prependKey?: Path,
12
9
  actionOnEmptyIntersect?: ActionOnEmptyIntersect
13
- ): T[]|undefined {
14
- if (!sourceArr && !targetArr) { return; }
10
+ ): T[] | undefined {
11
+ if (!sourceArr && !targetArr) {
12
+ return;
13
+ }
15
14
  if (handle === "target") {
16
15
  return targetArr;
17
16
  } else if (handle === "source") {
18
17
  return sourceArr;
19
18
  } else if (handle === "combine") {
20
- if (!sourceArr || !Array.isArray(sourceArr)) { return targetArr; }
21
- if (!targetArr || !Array.isArray(targetArr)) { return sourceArr; }
19
+ if (!sourceArr || !Array.isArray(sourceArr)) {
20
+ return targetArr;
21
+ }
22
+ if (!targetArr || !Array.isArray(targetArr)) {
23
+ return sourceArr;
24
+ }
22
25
  const arr = targetArr.concat(sourceArr);
23
26
  return [...new Set(arr)];
24
27
  } else if (handle === "intersect" || handle === "intersectOrFull") {
@@ -27,17 +30,21 @@ export function mergeArrays<T> (
27
30
 
28
31
  if ((targetIsArray || sourceIsArray) && handle === "intersect") {
29
32
  if (actionOnEmptyIntersect) {
30
- actionOnEmptyIntersect(targetArr as unknown, sourceArr, prependKey || []);
33
+ actionOnEmptyIntersect(
34
+ targetArr as unknown,
35
+ sourceArr,
36
+ prependKey || []
37
+ );
31
38
  }
32
39
  return;
33
40
  }
34
41
 
35
42
  if (handle === "intersectOrFull") {
36
- const val = (!targetIsArray) ? targetArr : sourceArr;
43
+ const val = !targetIsArray ? targetArr : sourceArr;
37
44
  return val;
38
45
  }
39
46
 
40
- return targetArr.filter(val => sourceArr.includes(val));
47
+ return targetArr.filter((val) => sourceArr.includes(val));
41
48
  }
42
49
  return undefined;
43
- }
50
+ }
@@ -0,0 +1,102 @@
1
+ import _merge from "lodash/merge.js";
2
+ import _isEmpty from "lodash/isEmpty.js";
3
+ import type { Query } from "@feathersjs/feathers";
4
+ import {
5
+ handleArray,
6
+ handleCircular,
7
+ makeDefaultOptions,
8
+ moveProperty,
9
+ } from "./utils";
10
+ import type { MergeQueryOptions } from "./types";
11
+ import { filterQuery } from "../filterQuery";
12
+
13
+ export function mergeQuery<T>(
14
+ target: Query,
15
+ source: Query,
16
+ options?: Partial<MergeQueryOptions<T>>
17
+ ): Query {
18
+ const fullOptions = makeDefaultOptions(options);
19
+ const { filters: targetFilters, query: targetQuery } = filterQuery(target, {
20
+ operators: fullOptions.operators,
21
+ service: fullOptions.service,
22
+ });
23
+
24
+ moveProperty(targetFilters, targetQuery, "$or");
25
+ moveProperty(targetFilters, targetQuery, "$and");
26
+
27
+ if (target.$limit) {
28
+ targetFilters.$limit = target.$limit;
29
+ }
30
+
31
+ let {
32
+ // eslint-disable-next-line prefer-const
33
+ filters: sourceFilters,
34
+ query: sourceQuery,
35
+ } = filterQuery(source, {
36
+ operators: fullOptions.operators,
37
+ service: fullOptions.service,
38
+ });
39
+
40
+ moveProperty(sourceFilters, sourceQuery, "$or");
41
+ moveProperty(sourceFilters, sourceQuery, "$and");
42
+
43
+ if (source.$limit) {
44
+ sourceFilters.$limit = source.$limit;
45
+ }
46
+
47
+ //#region filters
48
+
49
+ if (
50
+ target &&
51
+ !Object.prototype.hasOwnProperty.call(target, "$limit") &&
52
+ Object.prototype.hasOwnProperty.call(targetFilters, "$limit")
53
+ ) {
54
+ delete targetFilters.$limit;
55
+ }
56
+
57
+ if (
58
+ source &&
59
+ !Object.prototype.hasOwnProperty.call(source, "$limit") &&
60
+ Object.prototype.hasOwnProperty.call(sourceFilters, "$limit")
61
+ ) {
62
+ delete sourceFilters.$limit;
63
+ }
64
+
65
+ handleArray(targetFilters, sourceFilters, ["$select"], fullOptions);
66
+ // remaining filters
67
+ delete sourceFilters["$select"];
68
+ _merge(targetFilters, sourceFilters);
69
+
70
+ //#endregion
71
+
72
+ //#region '$or' / '$and'
73
+
74
+ if (
75
+ options?.useLogicalConjunction &&
76
+ (options.defaultHandle === "combine" ||
77
+ options.defaultHandle === "intersect") &&
78
+ !_isEmpty(targetQuery)
79
+ ) {
80
+ const logicalOp = options.defaultHandle === "combine" ? "$or" : "$and";
81
+ if (Object.prototype.hasOwnProperty.call(sourceQuery, logicalOp)) {
82
+ // omit '$or'/'$and' and put all other props into '$or'/'$and'
83
+ const andOr = sourceQuery[logicalOp] as unknown[];
84
+ delete sourceQuery[logicalOp];
85
+ andOr.push(sourceQuery);
86
+ sourceQuery = { [logicalOp]: andOr };
87
+ } else {
88
+ sourceQuery = { [logicalOp]: [sourceQuery] };
89
+ }
90
+ }
91
+
92
+ //#endregion
93
+
94
+ const keys = Object.keys(sourceQuery);
95
+ for (let i = 0, n = keys.length; i < n; i++) {
96
+ const key = keys[i];
97
+ handleCircular(targetQuery, sourceQuery, [key], fullOptions);
98
+ }
99
+ const result = Object.assign({}, targetFilters, targetQuery) as Query;
100
+
101
+ return result;
102
+ }
@@ -0,0 +1,25 @@
1
+ import type { Path } from "../../typesInternal";
2
+ import type { FilterQueryOptions } from "../filterQuery";
3
+
4
+ export type Handle =
5
+ | "target"
6
+ | "source"
7
+ | "combine"
8
+ | "intersect"
9
+ | "intersectOrFull";
10
+ export type FirstLast = "first" | "last";
11
+
12
+ export type ActionOnEmptyIntersect = (
13
+ target: unknown,
14
+ source: unknown,
15
+ prependKey: Path
16
+ ) => void;
17
+
18
+ export interface MergeQueryOptions<T> extends FilterQueryOptions<T> {
19
+ defaultHandle: Handle;
20
+ actionOnEmptyIntersect: ActionOnEmptyIntersect;
21
+ useLogicalConjunction: boolean;
22
+ handle?: {
23
+ [key: string]: Handle;
24
+ };
25
+ }