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