feathers-utils 2.0.0-9 → 2.1.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 (156) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -1
  3. package/dist/index.cjs +964 -0
  4. package/dist/index.d.ts +262 -5
  5. package/dist/index.mjs +938 -0
  6. package/package.json +35 -38
  7. package/src/filters/object.ts +1 -1
  8. package/src/hooks/forEach.ts +47 -0
  9. package/src/hooks/index.ts +3 -0
  10. package/src/hooks/makeSequelizeQuery.ts_ +4 -4
  11. package/src/hooks/parseFields.ts +27 -0
  12. package/src/hooks/runPerItem.ts +4 -6
  13. package/src/hooks/setData.ts +4 -5
  14. package/src/index.ts +1 -0
  15. package/src/typesInternal.ts +7 -0
  16. package/src/utility-types/index.ts +116 -0
  17. package/src/utils/getItemsIsArray.ts +27 -11
  18. package/src/utils/getPaginate.ts +2 -1
  19. package/src/utils/internal.utils.ts +6 -0
  20. package/src/utils/mergeQuery/mergeQuery.ts +62 -25
  21. package/src/utils/mergeQuery/utils.ts +82 -29
  22. package/dist/esm/filters/array.d.ts +0 -2
  23. package/dist/esm/filters/array.js +0 -17
  24. package/dist/esm/filters/index.d.ts +0 -2
  25. package/dist/esm/filters/index.js +0 -2
  26. package/dist/esm/filters/object.d.ts +0 -2
  27. package/dist/esm/filters/object.js +0 -15
  28. package/dist/esm/hooks/checkMulti.d.ts +0 -5
  29. package/dist/esm/hooks/checkMulti.js +0 -20
  30. package/dist/esm/hooks/createRelated.d.ts +0 -12
  31. package/dist/esm/hooks/createRelated.js +0 -31
  32. package/dist/esm/hooks/index.d.ts +0 -6
  33. package/dist/esm/hooks/index.js +0 -6
  34. package/dist/esm/hooks/onDelete.d.ts +0 -12
  35. package/dist/esm/hooks/onDelete.js +0 -47
  36. package/dist/esm/hooks/removeRelated.d.ts +0 -11
  37. package/dist/esm/hooks/removeRelated.js +0 -37
  38. package/dist/esm/hooks/runPerItem.d.ts +0 -10
  39. package/dist/esm/hooks/runPerItem.js +0 -29
  40. package/dist/esm/hooks/setData.d.ts +0 -11
  41. package/dist/esm/hooks/setData.js +0 -46
  42. package/dist/esm/index.d.ts +0 -5
  43. package/dist/esm/index.js +0 -5
  44. package/dist/esm/mixins/debounce-mixin/DebouncedStore.d.ts +0 -18
  45. package/dist/esm/mixins/debounce-mixin/DebouncedStore.js +0 -46
  46. package/dist/esm/mixins/debounce-mixin/debounceMixin.d.ts +0 -3
  47. package/dist/esm/mixins/debounce-mixin/debounceMixin.js +0 -19
  48. package/dist/esm/mixins/debounce-mixin/index.d.ts +0 -3
  49. package/dist/esm/mixins/debounce-mixin/index.js +0 -3
  50. package/dist/esm/mixins/debounce-mixin/types.d.ts +0 -13
  51. package/dist/esm/mixins/debounce-mixin/types.js +0 -1
  52. package/dist/esm/mixins/index.d.ts +0 -1
  53. package/dist/esm/mixins/index.js +0 -1
  54. package/dist/esm/types.d.ts +0 -3
  55. package/dist/esm/types.js +0 -1
  56. package/dist/esm/typesInternal.d.ts +0 -3
  57. package/dist/esm/typesInternal.js +0 -3
  58. package/dist/esm/utils/filterQuery.d.ts +0 -8
  59. package/dist/esm/utils/filterQuery.js +0 -30
  60. package/dist/esm/utils/getItemsIsArray.d.ts +0 -10
  61. package/dist/esm/utils/getItemsIsArray.js +0 -16
  62. package/dist/esm/utils/getPaginate.d.ts +0 -9
  63. package/dist/esm/utils/getPaginate.js +0 -20
  64. package/dist/esm/utils/index.d.ts +0 -11
  65. package/dist/esm/utils/index.js +0 -11
  66. package/dist/esm/utils/isMulti.d.ts +0 -11
  67. package/dist/esm/utils/isMulti.js +0 -26
  68. package/dist/esm/utils/isPaginated.d.ts +0 -5
  69. package/dist/esm/utils/isPaginated.js +0 -11
  70. package/dist/esm/utils/markHookForSkip.d.ts +0 -7
  71. package/dist/esm/utils/markHookForSkip.js +0 -18
  72. package/dist/esm/utils/mergeQuery/index.d.ts +0 -3
  73. package/dist/esm/utils/mergeQuery/index.js +0 -3
  74. package/dist/esm/utils/mergeQuery/mergeArrays.d.ts +0 -3
  75. package/dist/esm/utils/mergeQuery/mergeArrays.js +0 -37
  76. package/dist/esm/utils/mergeQuery/mergeQuery.d.ts +0 -3
  77. package/dist/esm/utils/mergeQuery/mergeQuery.js +0 -70
  78. package/dist/esm/utils/mergeQuery/types.d.ts +0 -13
  79. package/dist/esm/utils/mergeQuery/types.js +0 -1
  80. package/dist/esm/utils/mergeQuery/utils.d.ts +0 -11
  81. package/dist/esm/utils/mergeQuery/utils.js +0 -272
  82. package/dist/esm/utils/pushSet.d.ts +0 -8
  83. package/dist/esm/utils/pushSet.js +0 -22
  84. package/dist/esm/utils/setResultEmpty.d.ts +0 -5
  85. package/dist/esm/utils/setResultEmpty.js +0 -28
  86. package/dist/esm/utils/shouldSkip.d.ts +0 -8
  87. package/dist/esm/utils/shouldSkip.js +0 -29
  88. package/dist/esm/utils/validateQueryProperty.d.ts +0 -5
  89. package/dist/esm/utils/validateQueryProperty.js +0 -23
  90. package/dist/filters/array.d.ts +0 -2
  91. package/dist/filters/array.js +0 -21
  92. package/dist/filters/index.d.ts +0 -2
  93. package/dist/filters/index.js +0 -18
  94. package/dist/filters/object.d.ts +0 -2
  95. package/dist/filters/object.js +0 -22
  96. package/dist/hooks/checkMulti.d.ts +0 -5
  97. package/dist/hooks/checkMulti.js +0 -24
  98. package/dist/hooks/createRelated.d.ts +0 -12
  99. package/dist/hooks/createRelated.js +0 -44
  100. package/dist/hooks/index.d.ts +0 -6
  101. package/dist/hooks/index.js +0 -22
  102. package/dist/hooks/onDelete.d.ts +0 -12
  103. package/dist/hooks/onDelete.js +0 -60
  104. package/dist/hooks/removeRelated.d.ts +0 -11
  105. package/dist/hooks/removeRelated.js +0 -50
  106. package/dist/hooks/runPerItem.d.ts +0 -10
  107. package/dist/hooks/runPerItem.js +0 -42
  108. package/dist/hooks/setData.d.ts +0 -11
  109. package/dist/hooks/setData.js +0 -54
  110. package/dist/index.js +0 -21
  111. package/dist/mixins/debounce-mixin/DebouncedStore.d.ts +0 -18
  112. package/dist/mixins/debounce-mixin/DebouncedStore.js +0 -65
  113. package/dist/mixins/debounce-mixin/debounceMixin.d.ts +0 -3
  114. package/dist/mixins/debounce-mixin/debounceMixin.js +0 -23
  115. package/dist/mixins/debounce-mixin/index.d.ts +0 -3
  116. package/dist/mixins/debounce-mixin/index.js +0 -19
  117. package/dist/mixins/debounce-mixin/types.d.ts +0 -13
  118. package/dist/mixins/debounce-mixin/types.js +0 -2
  119. package/dist/mixins/index.d.ts +0 -1
  120. package/dist/mixins/index.js +0 -17
  121. package/dist/types.d.ts +0 -3
  122. package/dist/types.js +0 -2
  123. package/dist/typesInternal.d.ts +0 -3
  124. package/dist/typesInternal.js +0 -4
  125. package/dist/utils/filterQuery.d.ts +0 -8
  126. package/dist/utils/filterQuery.js +0 -46
  127. package/dist/utils/getItemsIsArray.d.ts +0 -10
  128. package/dist/utils/getItemsIsArray.js +0 -20
  129. package/dist/utils/getPaginate.d.ts +0 -9
  130. package/dist/utils/getPaginate.js +0 -22
  131. package/dist/utils/index.d.ts +0 -11
  132. package/dist/utils/index.js +0 -27
  133. package/dist/utils/isMulti.d.ts +0 -11
  134. package/dist/utils/isMulti.js +0 -30
  135. package/dist/utils/isPaginated.d.ts +0 -5
  136. package/dist/utils/isPaginated.js +0 -15
  137. package/dist/utils/markHookForSkip.d.ts +0 -7
  138. package/dist/utils/markHookForSkip.js +0 -22
  139. package/dist/utils/mergeQuery/index.d.ts +0 -3
  140. package/dist/utils/mergeQuery/index.js +0 -19
  141. package/dist/utils/mergeQuery/mergeArrays.d.ts +0 -3
  142. package/dist/utils/mergeQuery/mergeArrays.js +0 -41
  143. package/dist/utils/mergeQuery/mergeQuery.d.ts +0 -3
  144. package/dist/utils/mergeQuery/mergeQuery.js +0 -77
  145. package/dist/utils/mergeQuery/types.d.ts +0 -13
  146. package/dist/utils/mergeQuery/types.js +0 -2
  147. package/dist/utils/mergeQuery/utils.d.ts +0 -11
  148. package/dist/utils/mergeQuery/utils.js +0 -287
  149. package/dist/utils/pushSet.d.ts +0 -8
  150. package/dist/utils/pushSet.js +0 -29
  151. package/dist/utils/setResultEmpty.d.ts +0 -5
  152. package/dist/utils/setResultEmpty.js +0 -32
  153. package/dist/utils/shouldSkip.d.ts +0 -8
  154. package/dist/utils/shouldSkip.js +0 -33
  155. package/dist/utils/validateQueryProperty.d.ts +0 -5
  156. package/dist/utils/validateQueryProperty.js +0 -25
package/dist/index.mjs ADDED
@@ -0,0 +1,938 @@
1
+ import { Forbidden, GeneralError, BadRequest, MethodNotAllowed } from '@feathersjs/errors';
2
+ import _merge from 'lodash/merge.js';
3
+ import _isEmpty from 'lodash/isEmpty.js';
4
+ import _get from 'lodash/get.js';
5
+ import _has from 'lodash/has.js';
6
+ import _set from 'lodash/set.js';
7
+ import _uniqWith from 'lodash/uniqWith.js';
8
+ import { deepEqual } from 'fast-equals';
9
+ import { filterQuery as filterQuery$1 } from '@feathersjs/adapter-commons';
10
+ import _isEqual from 'lodash/isEqual.js';
11
+ import { _ } from '@feathersjs/commons';
12
+ import { checkContext } from 'feathers-hooks-common';
13
+ import _debounce from 'lodash/debounce.js';
14
+ import _isObject from 'lodash/isObject.js';
15
+
16
+ function mergeArrays(targetArr, sourceArr, handle, prependKey, actionOnEmptyIntersect) {
17
+ if (!sourceArr && !targetArr) {
18
+ return;
19
+ }
20
+ if (handle === "target") {
21
+ return targetArr;
22
+ } else if (handle === "source") {
23
+ return sourceArr;
24
+ } else if (handle === "combine") {
25
+ if (!sourceArr || !Array.isArray(sourceArr)) {
26
+ return targetArr;
27
+ }
28
+ if (!targetArr || !Array.isArray(targetArr)) {
29
+ return sourceArr;
30
+ }
31
+ const arr = targetArr.concat(sourceArr);
32
+ return [...new Set(arr)];
33
+ } else if (handle === "intersect" || handle === "intersectOrFull") {
34
+ const targetIsArray = !targetArr || !Array.isArray(targetArr);
35
+ const sourceIsArray = !sourceArr || !Array.isArray(sourceArr);
36
+ if ((targetIsArray || sourceIsArray) && handle === "intersect") {
37
+ if (actionOnEmptyIntersect) {
38
+ actionOnEmptyIntersect(
39
+ targetArr,
40
+ sourceArr,
41
+ prependKey || []
42
+ );
43
+ }
44
+ return;
45
+ }
46
+ if (handle === "intersectOrFull") {
47
+ const val = !targetIsArray ? targetArr : sourceArr;
48
+ return val;
49
+ }
50
+ return targetArr.filter((val) => sourceArr.includes(val));
51
+ }
52
+ return void 0;
53
+ }
54
+
55
+ const hasOwnProperty = (obj, ...keys) => {
56
+ return keys.some((x) => Object.prototype.hasOwnProperty.call(obj, x));
57
+ };
58
+
59
+ function handleArray(target, source, key, options) {
60
+ const targetVal = _get(target, key);
61
+ const sourceVal = _get(source, key);
62
+ if (!sourceVal && !targetVal) {
63
+ return;
64
+ }
65
+ const handle = _get(
66
+ options,
67
+ ["handle", ...key],
68
+ options.defaultHandle
69
+ );
70
+ const arr = mergeArrays(
71
+ targetVal,
72
+ sourceVal,
73
+ handle,
74
+ key,
75
+ options.actionOnEmptyIntersect
76
+ );
77
+ _set(target, key, arr);
78
+ }
79
+ function handleCircular(target, source, prependKey, options) {
80
+ if (target?.$or) {
81
+ target.$or = cleanOr(target.$or);
82
+ if (!target.$or) {
83
+ delete target.$or;
84
+ }
85
+ }
86
+ if (source?.$or) {
87
+ source.$or = cleanOr(source.$or);
88
+ if (!source.$or) {
89
+ delete source.$or;
90
+ }
91
+ }
92
+ if (target?.$and) {
93
+ target.$and = cleanAnd(target.$and);
94
+ if (!target.$and) {
95
+ delete target.$and;
96
+ }
97
+ }
98
+ if (source?.$and) {
99
+ source.$and = cleanAnd(source.$and);
100
+ if (!source.$and) {
101
+ delete source.$and;
102
+ }
103
+ }
104
+ if (!_has(source, prependKey)) {
105
+ return;
106
+ }
107
+ if (!_has(target, prependKey)) {
108
+ _set(target, prependKey, _get(source, prependKey));
109
+ return;
110
+ }
111
+ const { defaultHandle, actionOnEmptyIntersect } = options;
112
+ if (defaultHandle === "target") {
113
+ return;
114
+ }
115
+ const getTargetVal = () => {
116
+ return prependKey.length > 0 ? _get(target, prependKey) : target;
117
+ };
118
+ const getSourceVal = () => {
119
+ return prependKey.length > 0 ? _get(source, prependKey) : source;
120
+ };
121
+ const targetVal = getTargetVal();
122
+ const sourceVal = getSourceVal();
123
+ if (deepEqual(targetVal, sourceVal)) {
124
+ return;
125
+ }
126
+ if (defaultHandle === "source") {
127
+ _set(target, prependKey, sourceVal);
128
+ return;
129
+ }
130
+ if (targetVal === null || sourceVal === null) {
131
+ _set(target, prependKey, sourceVal);
132
+ return;
133
+ }
134
+ const typeOfTargetVal = typeof targetVal;
135
+ if (["boolean"].includes(typeOfTargetVal)) {
136
+ if (defaultHandle === "intersect") {
137
+ actionOnEmptyIntersect(target, source, prependKey);
138
+ }
139
+ _set(target, prependKey, sourceVal);
140
+ return;
141
+ }
142
+ const typeOfSourceVal = typeof sourceVal;
143
+ const isTargetSimple = ["string", "number"].includes(typeOfTargetVal);
144
+ const isSourceSimple = ["string", "number"].includes(typeOfSourceVal);
145
+ if (isTargetSimple || isSourceSimple) {
146
+ if (isTargetSimple && isSourceSimple) {
147
+ if (defaultHandle === "combine") {
148
+ _set(target, prependKey, { $in: [.../* @__PURE__ */ new Set([targetVal, sourceVal])] });
149
+ return;
150
+ } else if (defaultHandle === "intersect") {
151
+ actionOnEmptyIntersect(target, source, prependKey);
152
+ } else {
153
+ throw new Error("should not reach here");
154
+ }
155
+ } else if (hasOwnProperty(targetVal, "$in") || hasOwnProperty(sourceVal, "$in")) {
156
+ const targetHasIn = hasOwnProperty(targetVal, "$in");
157
+ const $in = targetHasIn ? targetVal["$in"] : sourceVal["$in"];
158
+ const otherVal = isTargetSimple ? targetVal : sourceVal;
159
+ if ($in.length === 1 && deepEqual($in[0], otherVal)) {
160
+ _set(target, prependKey, otherVal);
161
+ return;
162
+ } else if (defaultHandle === "combine") {
163
+ if (!$in.some((x) => deepEqual(x, otherVal))) {
164
+ $in.push(otherVal);
165
+ }
166
+ _set(target, `${prependKey}.$in`, $in);
167
+ return;
168
+ } else if (defaultHandle === "intersect") {
169
+ if ($in.some((x) => deepEqual(x, otherVal))) {
170
+ _set(target, prependKey, otherVal);
171
+ } else {
172
+ actionOnEmptyIntersect(target, source, prependKey);
173
+ }
174
+ return;
175
+ }
176
+ return;
177
+ }
178
+ }
179
+ const isTargetArray = Array.isArray(targetVal);
180
+ const isSourceArray = Array.isArray(sourceVal);
181
+ if (isTargetArray && isSourceArray) {
182
+ const key = prependKey[prependKey.length - 1];
183
+ if (key === "$or") {
184
+ if (defaultHandle === "combine") {
185
+ const newVals = sourceVal.filter(
186
+ (x) => !targetVal.some((y) => deepEqual(x, y))
187
+ );
188
+ targetVal.push(...newVals);
189
+ } else if (defaultHandle === "intersect") {
190
+ const targetParent = getParentProp(target, prependKey);
191
+ const sourceParent = getParentProp(source, prependKey);
192
+ targetParent.$and = targetParent.$and || [];
193
+ targetParent.$and.push({ $or: targetVal }, { $or: sourceVal });
194
+ targetParent.$and = cleanAnd(targetParent.$and);
195
+ if (!targetParent.$and) {
196
+ delete targetParent.$and;
197
+ }
198
+ delete targetParent.$or;
199
+ delete sourceParent.$or;
200
+ handleCircular(target, source, [...prependKey, "$and"], options);
201
+ return;
202
+ }
203
+ return;
204
+ } else if (key === "$and") {
205
+ if (defaultHandle === "combine") {
206
+ const targetParent = getParentProp(target, prependKey);
207
+ const sourceParent = getParentProp(source, prependKey);
208
+ targetParent.$or = targetParent.$or || [];
209
+ targetParent.$or.push({ $and: targetVal }, { $and: sourceVal });
210
+ targetParent.$or = cleanOr(targetParent.$or);
211
+ if (!targetParent.$or) {
212
+ delete targetParent.$or;
213
+ }
214
+ delete targetParent.$and;
215
+ delete sourceParent.$and;
216
+ handleCircular(target, source, [...prependKey, "$or"], options);
217
+ return;
218
+ } else if (defaultHandle === "intersect") {
219
+ const newVals = sourceVal.filter(
220
+ (x) => !targetVal.some((y) => deepEqual(x, y))
221
+ );
222
+ targetVal.push(...newVals);
223
+ return;
224
+ }
225
+ } else if (key === "$in") {
226
+ if (defaultHandle === "combine") {
227
+ let $in = targetVal.concat(sourceVal);
228
+ $in = [...new Set($in)];
229
+ _set(target, prependKey, $in);
230
+ return;
231
+ } else if (defaultHandle === "intersect") {
232
+ const $in = targetVal.filter(
233
+ (x) => sourceVal.some((y) => deepEqual(x, y))
234
+ );
235
+ if ($in.length === 0) {
236
+ actionOnEmptyIntersect(target, source, prependKey);
237
+ } else if ($in.length === 1) {
238
+ _set(target, prependKey.slice(0, -1), $in[0]);
239
+ return;
240
+ } else {
241
+ _set(target, prependKey, $in);
242
+ }
243
+ }
244
+ return;
245
+ }
246
+ _set(target, prependKey, sourceVal);
247
+ return;
248
+ }
249
+ if (typeOfTargetVal !== "object" || typeOfSourceVal !== "object") {
250
+ _set(target, prependKey, sourceVal);
251
+ return;
252
+ }
253
+ const sourceKeys = Object.keys(sourceVal);
254
+ for (let i = 0, n = sourceKeys.length; i < n; i++) {
255
+ const key = sourceKeys[i];
256
+ handleCircular(target, source, [...prependKey, key], options);
257
+ }
258
+ }
259
+ function makeDefaultOptions$1(options) {
260
+ options ?? (options = {});
261
+ options.defaultHandle ?? (options.defaultHandle = "combine");
262
+ options.useLogicalConjunction ?? (options.useLogicalConjunction = false);
263
+ options.actionOnEmptyIntersect ?? (options.actionOnEmptyIntersect = () => {
264
+ throw new Forbidden("You're not allowed to make this request");
265
+ });
266
+ options.handle = options.handle || {};
267
+ if (options.defaultHandle === "intersect") {
268
+ options.handle.$select = options.handle.$select || "intersectOrFull";
269
+ }
270
+ return options;
271
+ }
272
+ function moveProperty(from, to, ...keys) {
273
+ keys.forEach((key) => {
274
+ if (!hasOwnProperty(from, key)) {
275
+ return;
276
+ }
277
+ to[key] = from[key];
278
+ delete from[key];
279
+ });
280
+ }
281
+ function getParentProp(target, path) {
282
+ if (path.length <= 1) {
283
+ return target;
284
+ }
285
+ const pathOneUp = path.slice(0, -1);
286
+ return _get(target, pathOneUp);
287
+ }
288
+ function cleanOr(target) {
289
+ if (!target || !Array.isArray(target) || target.length <= 0) {
290
+ return target;
291
+ }
292
+ if (target.some((x) => _isEmpty(x))) {
293
+ return void 0;
294
+ } else {
295
+ return arrayWithoutDuplicates(target);
296
+ }
297
+ }
298
+ function cleanAnd(target) {
299
+ if (!target || !Array.isArray(target) || target.length <= 0) {
300
+ return target;
301
+ }
302
+ if (target.every((x) => _isEmpty(x))) {
303
+ return void 0;
304
+ } else {
305
+ target = target.filter((x) => !_isEmpty(x));
306
+ return arrayWithoutDuplicates(target);
307
+ }
308
+ }
309
+ function arrayWithoutDuplicates(target) {
310
+ if (!target || !Array.isArray(target)) {
311
+ return target;
312
+ }
313
+ return _uniqWith(target, deepEqual);
314
+ }
315
+ function isQueryMoreExplicitThanQuery(target, source) {
316
+ if (!target || !source) {
317
+ return;
318
+ }
319
+ const targetKeys = Object.keys(target);
320
+ const sourceKeys = Object.keys(source);
321
+ if (!sourceKeys.length) {
322
+ return target;
323
+ }
324
+ if (!targetKeys.length) {
325
+ return source;
326
+ }
327
+ if (targetKeys.every((key) => deepEqual(target[key], source[key]))) {
328
+ return source;
329
+ }
330
+ if (sourceKeys.every((key) => deepEqual(target[key], source[key]))) {
331
+ return target;
332
+ }
333
+ return;
334
+ }
335
+ function areQueriesOverlapping(target, source) {
336
+ if (!target || !source) {
337
+ return false;
338
+ }
339
+ const targetKeys = Object.keys(target);
340
+ const sourceKeys = Object.keys(source);
341
+ if (!sourceKeys.length || !targetKeys.length) {
342
+ return false;
343
+ }
344
+ if (targetKeys.some((x) => sourceKeys.includes(x)) || sourceKeys.some((x) => targetKeys.includes(x))) {
345
+ return true;
346
+ }
347
+ return false;
348
+ }
349
+
350
+ function filterQuery(query, _options) {
351
+ query = query || {};
352
+ _options = _options || {};
353
+ const { service, ...options } = _options;
354
+ if (service) {
355
+ const operators = options.operators ? options.operators : service.options?.operators;
356
+ const filters = options.filters ? options.filters : service.options?.filters;
357
+ const optionsForFilterQuery = {};
358
+ if (operators) {
359
+ optionsForFilterQuery.operators = operators;
360
+ }
361
+ if (filters) {
362
+ optionsForFilterQuery.filters = filters;
363
+ }
364
+ if (service && "filterQuery" in service && typeof service.filterQuery === "function") {
365
+ return service.filterQuery({ query }, optionsForFilterQuery);
366
+ } else {
367
+ return filterQuery$1(query, optionsForFilterQuery);
368
+ }
369
+ }
370
+ return filterQuery$1(query, options);
371
+ }
372
+
373
+ function mergeQuery(target, source, _options) {
374
+ const options = makeDefaultOptions$1(_options);
375
+ const { filters: targetFilters, query: targetQuery } = filterQuery(target, {
376
+ operators: options.operators,
377
+ filters: options.filters,
378
+ service: options.service
379
+ });
380
+ moveProperty(targetFilters, targetQuery, "$or", "$and");
381
+ if ("$limit" in target) {
382
+ targetFilters.$limit = target.$limit;
383
+ }
384
+ let {
385
+ // eslint-disable-next-line prefer-const
386
+ filters: sourceFilters,
387
+ query: sourceQuery
388
+ } = filterQuery(source, {
389
+ operators: options.operators,
390
+ filters: options.filters,
391
+ service: options.service
392
+ });
393
+ moveProperty(sourceFilters, sourceQuery, "$or", "$and");
394
+ if (source.$limit) {
395
+ sourceFilters.$limit = source.$limit;
396
+ }
397
+ if (target && !hasOwnProperty(target, "$limit") && hasOwnProperty(targetFilters, "$limit")) {
398
+ delete targetFilters.$limit;
399
+ }
400
+ if (source && !hasOwnProperty(source, "$limit") && hasOwnProperty(sourceFilters, "$limit")) {
401
+ delete sourceFilters.$limit;
402
+ }
403
+ handleArray(targetFilters, sourceFilters, ["$select"], options);
404
+ delete sourceFilters["$select"];
405
+ _merge(targetFilters, sourceFilters);
406
+ const subsetQuery = isQueryMoreExplicitThanQuery(targetQuery, sourceQuery);
407
+ if (options.defaultHandle === "intersect" && !!subsetQuery && !hasOwnProperty(targetQuery, "$or", "$and") && !hasOwnProperty(sourceQuery, "$or", "$and")) {
408
+ return {
409
+ ...targetFilters,
410
+ ...subsetQuery
411
+ };
412
+ }
413
+ if (options.defaultHandle === "intersect" && !areQueriesOverlapping(targetQuery, sourceQuery) && !hasOwnProperty(targetQuery, "$or", "$and") && !hasOwnProperty(sourceQuery, "$or", "$and")) {
414
+ return {
415
+ ...targetFilters,
416
+ ...targetQuery,
417
+ ...sourceQuery
418
+ };
419
+ }
420
+ if (options.useLogicalConjunction && (options.defaultHandle === "combine" || options.defaultHandle === "intersect") && !_isEmpty(targetQuery)) {
421
+ const logicalOp = options.defaultHandle === "combine" ? "$or" : "$and";
422
+ if (hasOwnProperty(sourceQuery, logicalOp)) {
423
+ const andOr = sourceQuery[logicalOp];
424
+ delete sourceQuery[logicalOp];
425
+ andOr.push(sourceQuery);
426
+ sourceQuery = { [logicalOp]: andOr };
427
+ } else {
428
+ sourceQuery = { [logicalOp]: [sourceQuery] };
429
+ }
430
+ }
431
+ const keys = Object.keys(sourceQuery);
432
+ for (let i = 0, n = keys.length; i < n; i++) {
433
+ const key = keys[i];
434
+ handleCircular(targetQuery, sourceQuery, [key], options);
435
+ }
436
+ return {
437
+ ...targetFilters,
438
+ ...targetQuery
439
+ };
440
+ }
441
+
442
+ const getItemsIsArray = (context, options) => {
443
+ const {
444
+ from = "automatic"
445
+ } = options || {};
446
+ let itemOrItems;
447
+ if (from === "automatic") {
448
+ itemOrItems = context.type === "before" ? context.data : context.result;
449
+ itemOrItems = itemOrItems && context.method === "find" ? itemOrItems.data || itemOrItems : itemOrItems;
450
+ } else if (from === "data") {
451
+ itemOrItems = context.data;
452
+ } else if (from === "result") {
453
+ itemOrItems = context.result;
454
+ }
455
+ const isArray = Array.isArray(itemOrItems);
456
+ return {
457
+ items: isArray ? itemOrItems : itemOrItems != null ? [itemOrItems] : [],
458
+ isArray
459
+ };
460
+ };
461
+
462
+ const getPaginate = (context) => {
463
+ if (hasOwnProperty(context.params, "paginate")) {
464
+ return context.params.paginate || void 0;
465
+ }
466
+ if (context.params.paginate === false) {
467
+ return void 0;
468
+ }
469
+ let options = context.service?.options || {};
470
+ options = {
471
+ ...options,
472
+ ...context.params.adapter
473
+ };
474
+ return options.paginate || void 0;
475
+ };
476
+
477
+ const isMulti = (context) => {
478
+ const { method } = context;
479
+ if (method === "find") {
480
+ return true;
481
+ } else if (["patch", "remove"].includes(method)) {
482
+ return context.id == null;
483
+ } else if (method === "create") {
484
+ const items = context.type === "before" ? context.data : context.result;
485
+ return Array.isArray(items);
486
+ } else if (["get", "update"].includes(method)) {
487
+ return false;
488
+ }
489
+ return false;
490
+ };
491
+
492
+ const isPaginated = (context) => {
493
+ if (context.params.paginate === false || context.method !== "find") {
494
+ return false;
495
+ }
496
+ const paginate = getPaginate(context);
497
+ return !!paginate;
498
+ };
499
+
500
+ const pushSet = (obj, path, val, options) => {
501
+ options = options || {};
502
+ let arr = _get(obj, path);
503
+ if (!arr || !Array.isArray(arr)) {
504
+ arr = [val];
505
+ _set(obj, path, arr);
506
+ return arr;
507
+ } else {
508
+ if (options.unique && arr.some((x) => _isEqual(x, val))) {
509
+ return arr;
510
+ }
511
+ arr.push(val);
512
+ return arr;
513
+ }
514
+ };
515
+
516
+ function markHookForSkip(hookName, type, context) {
517
+ context = context || {};
518
+ const params = context.params || {};
519
+ const types = Array.isArray(type) ? type : [type];
520
+ types.forEach((t) => {
521
+ const combinedName = t === "all" ? hookName : `${type}:${hookName}`;
522
+ pushSet(params, ["skipHooks"], combinedName, { unique: true });
523
+ });
524
+ context.params = params;
525
+ return context;
526
+ }
527
+
528
+ const setResultEmpty = (context) => {
529
+ if (context.result) {
530
+ return context;
531
+ }
532
+ const multi = isMulti(context);
533
+ if (multi) {
534
+ if (context.method === "find" && isPaginated(context)) {
535
+ context.result = {
536
+ total: 0,
537
+ skip: 0,
538
+ limit: 0,
539
+ data: []
540
+ };
541
+ } else {
542
+ context.result = [];
543
+ }
544
+ } else {
545
+ context.result = null;
546
+ }
547
+ return context;
548
+ };
549
+
550
+ const shouldSkip = (hookName, context, options) => {
551
+ if (!context.params || !context.params.skipHooks || options?.notSkippable) {
552
+ return false;
553
+ }
554
+ const { skipHooks } = context.params;
555
+ if (!Array.isArray(skipHooks)) {
556
+ throw new GeneralError("The `skipHooks` param must be an Array of Strings");
557
+ }
558
+ const { type } = context;
559
+ if (skipHooks.includes(hookName)) {
560
+ return true;
561
+ } else if (skipHooks.includes("all")) {
562
+ return true;
563
+ } else if (skipHooks.includes(type)) {
564
+ return true;
565
+ } else if (skipHooks.includes(`${type}:${hookName}`)) {
566
+ return true;
567
+ }
568
+ return false;
569
+ };
570
+
571
+ const isPlainObject = (value) => _.isObject(value) && value.constructor === {}.constructor;
572
+ const validateQueryProperty = (query, operators = []) => {
573
+ if (!isPlainObject(query)) {
574
+ return query;
575
+ }
576
+ for (const key of Object.keys(query)) {
577
+ if (key.startsWith("$") && !operators.includes(key)) {
578
+ throw new BadRequest(`Invalid query parameter ${key}`, query);
579
+ }
580
+ const value = query[key];
581
+ if (isPlainObject(value)) {
582
+ query[key] = validateQueryProperty(value, operators);
583
+ }
584
+ }
585
+ return {
586
+ ...query
587
+ };
588
+ };
589
+
590
+ function checkMulti() {
591
+ return (context) => {
592
+ if (shouldSkip("checkMulti", context)) {
593
+ return context;
594
+ }
595
+ const { service, method } = context;
596
+ if (!service.allowsMulti || !isMulti(context) || method === "find") {
597
+ return context;
598
+ }
599
+ if (!service.allowsMulti(method)) {
600
+ throw new MethodNotAllowed(`Can not ${method} multiple entries`);
601
+ }
602
+ return context;
603
+ };
604
+ }
605
+
606
+ function createRelated({
607
+ service,
608
+ multi = true,
609
+ data,
610
+ createItemsInDataArraySeparately = true
611
+ }) {
612
+ if (!service || !data) {
613
+ throw "initialize hook 'createRelated' completely!";
614
+ }
615
+ return async (context) => {
616
+ if (shouldSkip("createRelated", context)) {
617
+ return context;
618
+ }
619
+ checkContext(context, "after", void 0, "createRelated");
620
+ const { items } = getItemsIsArray(context);
621
+ let dataToCreate = (await Promise.all(items.map(async (item) => data(item, context)))).filter((x) => !!x);
622
+ if (createItemsInDataArraySeparately) {
623
+ dataToCreate = dataToCreate.flat();
624
+ }
625
+ if (!dataToCreate || dataToCreate.length <= 0) {
626
+ return context;
627
+ }
628
+ if (multi) {
629
+ await context.app.service(service).create(dataToCreate);
630
+ } else {
631
+ await Promise.all(
632
+ dataToCreate.map(
633
+ async (item) => context.app.service(service).create(item)
634
+ )
635
+ );
636
+ }
637
+ return context;
638
+ };
639
+ }
640
+
641
+ const forEach = (actionPerItem, _options) => {
642
+ const options = {
643
+ wait: "parallel",
644
+ items: "automatic",
645
+ ..._options
646
+ };
647
+ return async (context) => {
648
+ if (shouldSkip("runForItems", context)) {
649
+ return context;
650
+ }
651
+ const { items } = getItemsIsArray(context, { from: options.items });
652
+ const promises = [];
653
+ for (const item of items) {
654
+ const promise = actionPerItem(item, context);
655
+ if (options.wait === "sequential") {
656
+ await promise;
657
+ } else {
658
+ promises.push(promise);
659
+ }
660
+ }
661
+ if (options.wait === "parallel") {
662
+ await Promise.all(promises);
663
+ }
664
+ return context;
665
+ };
666
+ };
667
+
668
+ function onDelete(service, {
669
+ keyThere,
670
+ keyHere = "id",
671
+ onDelete: onDelete2 = "cascade",
672
+ blocking = true
673
+ }) {
674
+ if (!service || !keyThere) {
675
+ throw "initialize hook 'removeRelated' completely!";
676
+ }
677
+ if (!["cascade", "set null"].includes(onDelete2)) {
678
+ throw "onDelete must be 'cascade' or 'set null'";
679
+ }
680
+ return async (context) => {
681
+ if (shouldSkip("onDelete", context)) {
682
+ return context;
683
+ }
684
+ checkContext(context, "after", "remove", "onDelete");
685
+ const { items } = getItemsIsArray(context);
686
+ let ids = items.map((x) => x[keyHere]).filter((x) => !!x);
687
+ ids = [...new Set(ids)];
688
+ if (!ids || ids.length <= 0) {
689
+ return context;
690
+ }
691
+ const params = {
692
+ query: {
693
+ [keyThere]: {
694
+ $in: ids
695
+ }
696
+ },
697
+ paginate: false
698
+ };
699
+ let promise;
700
+ if (onDelete2 === "cascade") {
701
+ promise = context.app.service(service).remove(null, params);
702
+ } else if (onDelete2 === "set null") {
703
+ const data = { [keyThere]: null };
704
+ promise = context.app.service(service).patch(null, data, params);
705
+ }
706
+ if (blocking) {
707
+ await promise;
708
+ }
709
+ return context;
710
+ };
711
+ }
712
+
713
+ const parseFields = (type, options) => (context) => {
714
+ const { items } = getItemsIsArray(context);
715
+ items.forEach((item) => {
716
+ options.fields.forEach((field) => {
717
+ if (!(field in item)) {
718
+ return;
719
+ }
720
+ if (type === "date") {
721
+ item[field] = new Date(item[field]);
722
+ } else if (type === "number") {
723
+ item[field] = Number(item[field]);
724
+ }
725
+ });
726
+ });
727
+ return context;
728
+ };
729
+
730
+ function removeRelated({
731
+ service,
732
+ keyThere,
733
+ keyHere = "id",
734
+ blocking = true
735
+ }) {
736
+ if (!service || !keyThere) {
737
+ throw "initialize hook 'removeRelated' completely!";
738
+ }
739
+ return async (context) => {
740
+ if (shouldSkip("removeRelated", context)) {
741
+ return context;
742
+ }
743
+ checkContext(context, "after", "remove", "removeRelated");
744
+ const { items } = getItemsIsArray(context);
745
+ let ids = items.map((x) => x[keyHere]).filter((x) => !!x);
746
+ ids = [...new Set(ids)];
747
+ if (!ids || ids.length <= 0) {
748
+ return context;
749
+ }
750
+ const promise = context.app.service(service).remove(null, {
751
+ query: {
752
+ [keyThere]: {
753
+ $in: ids
754
+ }
755
+ },
756
+ paginate: false
757
+ });
758
+ if (blocking) {
759
+ await promise;
760
+ }
761
+ return context;
762
+ };
763
+ }
764
+
765
+ const makeOptions = (options) => {
766
+ options = options || {};
767
+ return {
768
+ wait: true,
769
+ ...options
770
+ };
771
+ };
772
+ const runPerItem = (actionPerItem, _options) => {
773
+ const options = makeOptions(_options);
774
+ return async (context) => {
775
+ if (shouldSkip("runForItems", context)) {
776
+ return context;
777
+ }
778
+ const { items } = getItemsIsArray(context);
779
+ const promises = items.map(async (item) => {
780
+ await actionPerItem(item, context);
781
+ });
782
+ if (options.wait) {
783
+ await Promise.all(promises);
784
+ }
785
+ return context;
786
+ };
787
+ };
788
+
789
+ const defaultOptions = {
790
+ allowUndefined: false,
791
+ overwrite: true
792
+ };
793
+ function setData(from, to, _options) {
794
+ const options = {
795
+ ...defaultOptions,
796
+ ..._options
797
+ };
798
+ return (context) => {
799
+ if (shouldSkip("setData", context)) {
800
+ return context;
801
+ }
802
+ const { items } = getItemsIsArray(context);
803
+ if (!_has(context, from)) {
804
+ if (!context.params?.provider || options.allowUndefined === true) {
805
+ return context;
806
+ }
807
+ if (!options.overwrite && items.every((item) => _has(item, to))) {
808
+ return context;
809
+ }
810
+ throw new Forbidden(`Expected field ${from.toString()} not available`);
811
+ }
812
+ const val = _get(context, from);
813
+ items.forEach((item) => {
814
+ let overwrite;
815
+ if (typeof options.overwrite === "function") {
816
+ overwrite = options.overwrite(item, context);
817
+ } else {
818
+ overwrite = options.overwrite;
819
+ }
820
+ if (!overwrite && _has(item, to)) {
821
+ return;
822
+ }
823
+ _set(item, to, val);
824
+ });
825
+ return context;
826
+ };
827
+ }
828
+
829
+ const makeDefaultOptions = () => {
830
+ return {
831
+ leading: false,
832
+ maxWait: void 0,
833
+ trailing: true,
834
+ wait: 100
835
+ };
836
+ };
837
+ class DebouncedStore {
838
+ constructor(app, options) {
839
+ this._app = app;
840
+ this._options = Object.assign(makeDefaultOptions(), options);
841
+ this._queueById = {};
842
+ this._isRunningById = {};
843
+ this.add = this.debounceById(this.unbounced, this._options.wait, {
844
+ leading: this._options.leading,
845
+ maxWait: this._options.maxWait,
846
+ trailing: this._options.trailing
847
+ });
848
+ }
849
+ async unbounced(id, action) {
850
+ if (this._queueById[id] === void 0) {
851
+ return;
852
+ }
853
+ delete this._queueById[id];
854
+ this._isRunningById[id] = true;
855
+ await action(this._app);
856
+ delete this._isRunningById[id];
857
+ }
858
+ debounceById(func, wait, options) {
859
+ return (id, action) => {
860
+ if (typeof this._queueById[id] === "function") {
861
+ return this._queueById[id](id, action);
862
+ }
863
+ this._queueById[id] = _debounce(
864
+ (id2, action2) => {
865
+ this.unbounced(id2, action2);
866
+ },
867
+ wait,
868
+ { ...options, leading: false }
869
+ );
870
+ return this._queueById[id](id, action);
871
+ };
872
+ }
873
+ cancel(id) {
874
+ delete this._queueById[id];
875
+ }
876
+ }
877
+
878
+ function debounceMixin(options) {
879
+ return (app) => {
880
+ options = options || {};
881
+ const defaultOptions = Object.assign(
882
+ makeDefaultOptions(),
883
+ options?.default
884
+ );
885
+ app.mixins.push((service, path) => {
886
+ if (options?.blacklist && options.blacklist.includes(path))
887
+ return;
888
+ if (service.debouncedStore) {
889
+ console.warn(
890
+ `[feathers-utils] service: '${path}' already has a property 'debouncedStore'. Mixin will skip creating a new debouncedStore`
891
+ );
892
+ return;
893
+ }
894
+ const serviceOptions = Object.assign({}, defaultOptions, options?.[path]);
895
+ service.debouncedStore = new DebouncedStore(
896
+ app,
897
+ serviceOptions
898
+ );
899
+ });
900
+ };
901
+ }
902
+
903
+ const filterQueryArray = (key) => (arr, { operators }) => {
904
+ if (arr && !Array.isArray(arr)) {
905
+ throw new Error(
906
+ `Invalid query parameter '${key}'. It has to be an array`
907
+ );
908
+ }
909
+ if (Array.isArray(arr)) {
910
+ return arr.map((current) => validateQueryProperty(current, operators));
911
+ }
912
+ return arr;
913
+ };
914
+ const filterArray = (...keys) => {
915
+ const result = {};
916
+ for (const key of keys) {
917
+ result[key] = filterQueryArray(key);
918
+ }
919
+ return result;
920
+ };
921
+
922
+ const filterQueryObject = (key) => (obj, { operators }) => {
923
+ if (obj && !_isObject(obj)) {
924
+ throw new Error(
925
+ `Invalid query parameter: '${key}'. It has to be an object`
926
+ );
927
+ }
928
+ return validateQueryProperty(obj, operators);
929
+ };
930
+ const filterObject = (...keys) => {
931
+ const result = {};
932
+ for (const key of keys) {
933
+ result[key] = filterQueryObject(key);
934
+ }
935
+ return result;
936
+ };
937
+
938
+ export { DebouncedStore, checkMulti, createRelated, debounceMixin, filterArray, filterObject, filterQuery, forEach, getItemsIsArray, getPaginate, isMulti, isPaginated, makeDefaultOptions, markHookForSkip, mergeArrays, mergeQuery, onDelete, parseFields, pushSet, removeRelated, runPerItem, setData, setResultEmpty, shouldSkip, validateQueryProperty };