feathers-utils 1.6.1 → 2.0.0-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.
- package/dist/esm/hooks/checkMulti.d.ts +2 -0
- package/dist/esm/hooks/checkMulti.js +18 -0
- package/dist/esm/hooks/runPerItem.d.ts +4 -0
- package/dist/esm/hooks/runPerItem.js +26 -0
- package/dist/esm/hooks/setData.d.ts +4 -0
- package/dist/esm/hooks/setData.js +34 -0
- package/dist/esm/index.d.ts +30 -0
- package/dist/esm/index.js +30 -0
- package/dist/esm/mixins/debounce-mixin/DebouncedStore.d.ts +15 -0
- package/dist/esm/mixins/debounce-mixin/DebouncedStore.js +46 -0
- package/dist/esm/mixins/debounce-mixin/index.d.ts +8 -0
- package/dist/esm/mixins/debounce-mixin/index.js +20 -0
- package/dist/esm/types.d.ts +60 -0
- package/dist/esm/types.js +2 -0
- package/dist/esm/utils/filterQuery.d.ts +3 -0
- package/dist/esm/utils/filterQuery.js +28 -0
- package/dist/esm/utils/getPaginate.d.ts +7 -0
- package/dist/esm/utils/getPaginate.js +14 -0
- package/dist/esm/utils/isMulti.d.ts +2 -0
- package/dist/esm/utils/isMulti.js +17 -0
- package/dist/esm/utils/isPaginated.d.ts +2 -0
- package/dist/esm/utils/isPaginated.js +8 -0
- package/dist/esm/utils/markHookForSkip.d.ts +3 -0
- package/dist/esm/utils/markHookForSkip.js +14 -0
- package/dist/esm/utils/mergeQuery/index.d.ts +3 -0
- package/dist/esm/utils/mergeQuery/index.js +327 -0
- package/dist/esm/utils/mergeQuery/mergeArrays.d.ts +2 -0
- package/dist/esm/utils/mergeQuery/mergeArrays.js +37 -0
- package/dist/esm/utils/pushSet.d.ts +2 -0
- package/dist/esm/utils/pushSet.js +19 -0
- package/dist/esm/utils/setResultEmpty.d.ts +2 -0
- package/dist/esm/utils/setResultEmpty.js +25 -0
- package/dist/esm/utils/shouldSkip.d.ts +2 -0
- package/dist/esm/utils/shouldSkip.js +29 -0
- package/dist/hooks/checkMulti.js +2 -2
- package/dist/hooks/runPerItem.d.ts +2 -1
- package/dist/hooks/runPerItem.js +3 -2
- package/dist/hooks/setData.js +7 -6
- package/dist/index.d.ts +6 -3
- package/dist/index.js +7 -3
- package/dist/mixins/debounce-mixin/DebouncedStore.d.ts +2 -2
- package/dist/mixins/debounce-mixin/DebouncedStore.js +2 -2
- package/dist/mixins/debounce-mixin/index.d.ts +4 -1
- package/dist/mixins/debounce-mixin/index.js +1 -1
- package/dist/types.d.ts +3 -2
- package/dist/utils/filterQuery.js +2 -2
- package/dist/utils/getPaginate.d.ts +7 -0
- package/dist/utils/getPaginate.js +15 -0
- package/dist/utils/isMulti.js +2 -3
- package/dist/utils/isPaginated.d.ts +2 -0
- package/dist/utils/isPaginated.js +12 -0
- package/dist/utils/markHookForSkip.js +1 -1
- package/dist/utils/mergeQuery/index.js +38 -38
- package/dist/utils/pushSet.js +3 -3
- package/dist/utils/setResultEmpty.d.ts +2 -0
- package/dist/utils/setResultEmpty.js +29 -0
- package/package.json +19 -13
- package/src/hooks/runPerItem.ts +4 -1
- package/src/hooks/setData.ts +1 -0
- package/src/index.ts +6 -3
- package/src/mixins/debounce-mixin/DebouncedStore.ts +1 -1
- package/src/mixins/debounce-mixin/index.ts +7 -3
- package/src/types.ts +4 -3
- package/src/utils/filterQuery.ts +2 -2
- package/src/utils/getPaginate.ts +26 -0
- package/src/utils/isMulti.ts +2 -3
- package/src/utils/isPaginated.ts +12 -0
- package/src/utils/setResultEmpty.ts +28 -0
- package/tsconfig-esm.json +9 -0
- package/.github/workflows/node.js.yml +0 -45
- package/.vscode/launch.json +0 -28
- package/.vscode/settings.json +0 -14
- package/dist/utils/addHook.d.ts +0 -3
- package/dist/utils/addHook.js +0 -40
- package/src/utils/addHook.ts +0 -49
- package/test/hooks/checkMulti.test.ts +0 -121
- package/test/hooks/setData.test.ts +0 -333
- package/test/mixins/debounce-mixin.test.ts +0 -174
- package/test/utils/addHook.test.ts +0 -307
- package/test/utils/isMulti.test.ts +0 -53
- package/test/utils/markHookForSkip.test.ts +0 -292
- package/test/utils/mergeQuery.test.ts +0 -407
- package/test/utils/pushSet.test.ts +0 -66
- package/test/utils/shouldSkip.test.ts +0 -67
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import _get from "lodash/get";
|
|
2
|
+
import _has from "lodash/has";
|
|
3
|
+
import _isEmpty from "lodash/isEmpty";
|
|
4
|
+
import _isEqual from "lodash/isEqual";
|
|
5
|
+
import _merge from "lodash/merge";
|
|
6
|
+
import _set from "lodash/set";
|
|
7
|
+
import _uniqWith from "lodash/uniqWith";
|
|
8
|
+
import { mergeArrays } from "./mergeArrays";
|
|
9
|
+
import { filterQuery } from "../filterQuery";
|
|
10
|
+
import { Forbidden } from "@feathersjs/errors";
|
|
11
|
+
const hasOwnProperty = (obj, key) => {
|
|
12
|
+
return Object.prototype.hasOwnProperty.call(obj, key);
|
|
13
|
+
};
|
|
14
|
+
function handleArray(target, source, key, options) {
|
|
15
|
+
const targetVal = _get(target, key);
|
|
16
|
+
const sourceVal = _get(source, key);
|
|
17
|
+
if (!sourceVal && !targetVal) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const handle = _get(options, ["handle", ...key], options.defaultHandle);
|
|
21
|
+
const arr = mergeArrays(targetVal, sourceVal, handle, key, options.actionOnEmptyIntersect);
|
|
22
|
+
_set(target, key, arr);
|
|
23
|
+
}
|
|
24
|
+
function handleCircular(target, source, prependKey, options) {
|
|
25
|
+
if (target?.$or) {
|
|
26
|
+
target.$or = cleanOr(target.$or);
|
|
27
|
+
if (!target.$or) {
|
|
28
|
+
delete target.$or;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (source?.$or) {
|
|
32
|
+
source.$or = cleanOr(source.$or);
|
|
33
|
+
if (!source.$or) {
|
|
34
|
+
delete source.$or;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (target?.$and) {
|
|
38
|
+
target.$and = cleanAnd(target.$and);
|
|
39
|
+
if (!target.$and) {
|
|
40
|
+
delete target.$and;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (source?.$and) {
|
|
44
|
+
source.$and = cleanAnd(source.$and);
|
|
45
|
+
if (!source.$and) {
|
|
46
|
+
delete source.$and;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (!_has(source, prependKey)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!_has(target, prependKey)) {
|
|
53
|
+
_set(target, prependKey, _get(source, prependKey));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const { defaultHandle, actionOnEmptyIntersect } = options;
|
|
57
|
+
if (defaultHandle === "target") {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const getTargetVal = () => {
|
|
61
|
+
return (prependKey.length > 0) ? _get(target, prependKey) : target;
|
|
62
|
+
};
|
|
63
|
+
const getSourceVal = () => {
|
|
64
|
+
return (prependKey.length > 0) ? _get(source, prependKey) : source;
|
|
65
|
+
};
|
|
66
|
+
const targetVal = getTargetVal();
|
|
67
|
+
const sourceVal = getSourceVal();
|
|
68
|
+
if (_isEqual(targetVal, sourceVal)) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (defaultHandle === "source") {
|
|
72
|
+
_set(target, prependKey, sourceVal);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (targetVal === null || sourceVal === null) {
|
|
76
|
+
_set(target, prependKey, sourceVal);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const typeOfTargetVal = typeof targetVal;
|
|
80
|
+
if (["boolean"].includes(typeOfTargetVal)) {
|
|
81
|
+
if (defaultHandle === "intersect") {
|
|
82
|
+
actionOnEmptyIntersect(target, source, prependKey);
|
|
83
|
+
}
|
|
84
|
+
_set(target, prependKey, sourceVal);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const typeOfSourceVal = typeof sourceVal;
|
|
88
|
+
const isTargetSimple = ["string", "number"].includes(typeOfTargetVal);
|
|
89
|
+
const isSourceSimple = ["string", "number"].includes(typeOfSourceVal);
|
|
90
|
+
if (isTargetSimple || isSourceSimple) {
|
|
91
|
+
if (isTargetSimple && isSourceSimple) {
|
|
92
|
+
if (defaultHandle === "combine") {
|
|
93
|
+
_set(target, prependKey, { $in: [...new Set([targetVal, sourceVal])] });
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
else if (defaultHandle === "intersect") {
|
|
97
|
+
actionOnEmptyIntersect(target, source, prependKey);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
throw new Error("should not reach here");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (hasOwnProperty(targetVal, "$in") || hasOwnProperty(sourceVal, "$in")) {
|
|
104
|
+
const targetHasIn = hasOwnProperty(targetVal, "$in");
|
|
105
|
+
const $in = (targetHasIn) ? targetVal["$in"] : sourceVal["$in"];
|
|
106
|
+
const otherVal = (isTargetSimple) ? targetVal : sourceVal;
|
|
107
|
+
if ($in.length === 1 && _isEqual($in[0], otherVal)) {
|
|
108
|
+
_set(target, prependKey, otherVal);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
else if (defaultHandle === "combine") {
|
|
112
|
+
if (!$in.some((x) => _isEqual(x, otherVal))) {
|
|
113
|
+
$in.push(otherVal);
|
|
114
|
+
}
|
|
115
|
+
_set(target, `${prependKey}.$in`, $in);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
else if (defaultHandle === "intersect") {
|
|
119
|
+
if ($in.some((x) => _isEqual(x, otherVal))) {
|
|
120
|
+
_set(target, prependKey, otherVal);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
actionOnEmptyIntersect(target, source, prependKey);
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const isTargetArray = Array.isArray(targetVal);
|
|
131
|
+
const isSourceArray = Array.isArray(sourceVal);
|
|
132
|
+
if (isTargetArray && isSourceArray) {
|
|
133
|
+
const key = prependKey[prependKey.length - 1];
|
|
134
|
+
if (key === "$or") {
|
|
135
|
+
if (defaultHandle === "combine") {
|
|
136
|
+
const newVals = sourceVal.filter((x) => !targetVal.some((y) => _isEqual(x, y)));
|
|
137
|
+
targetVal.push(...newVals);
|
|
138
|
+
}
|
|
139
|
+
else if (defaultHandle === "intersect") {
|
|
140
|
+
// combine into "$and"
|
|
141
|
+
const targetParent = getParentProp(target, prependKey);
|
|
142
|
+
const sourceParent = getParentProp(source, prependKey);
|
|
143
|
+
targetParent.$and = targetParent.$and || [];
|
|
144
|
+
targetParent.$and.push({ $or: targetVal }, { $or: sourceVal });
|
|
145
|
+
targetParent.$and = cleanAnd(targetParent.$and);
|
|
146
|
+
if (!targetParent.$and) {
|
|
147
|
+
delete targetParent.$and;
|
|
148
|
+
}
|
|
149
|
+
delete targetParent.$or;
|
|
150
|
+
delete sourceParent.$or;
|
|
151
|
+
handleCircular(target, source, [...prependKey, "$and"], options);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
else if (key === "$and") {
|
|
157
|
+
if (defaultHandle === "combine") {
|
|
158
|
+
// combine into "$or"
|
|
159
|
+
const targetParent = getParentProp(target, prependKey);
|
|
160
|
+
const sourceParent = getParentProp(source, prependKey);
|
|
161
|
+
targetParent.$or = targetParent.$or || [];
|
|
162
|
+
targetParent.$or.push({ $and: targetVal }, { $and: sourceVal });
|
|
163
|
+
targetParent.$or = cleanOr(targetParent.$or);
|
|
164
|
+
if (!targetParent.$or) {
|
|
165
|
+
delete targetParent.$or;
|
|
166
|
+
}
|
|
167
|
+
delete targetParent.$and;
|
|
168
|
+
delete sourceParent.$and;
|
|
169
|
+
handleCircular(target, source, [...prependKey, "$or"], options);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
else if (defaultHandle === "intersect") {
|
|
173
|
+
const newVals = sourceVal.filter((x) => !targetVal.some((y) => _isEqual(x, y)));
|
|
174
|
+
targetVal.push(...newVals);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
else if (key === "$in") {
|
|
179
|
+
if (defaultHandle === "combine") {
|
|
180
|
+
let $in = targetVal.concat(sourceVal);
|
|
181
|
+
$in = [...new Set($in)];
|
|
182
|
+
_set(target, prependKey, $in);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
else if (defaultHandle === "intersect") {
|
|
186
|
+
const $in = targetVal.filter((x) => sourceVal.some((y) => _isEqual(x, y)));
|
|
187
|
+
if ($in.length === 0) {
|
|
188
|
+
actionOnEmptyIntersect(target, source, prependKey);
|
|
189
|
+
}
|
|
190
|
+
else if ($in.length === 1) {
|
|
191
|
+
_set(target, prependKey.slice(0, -1), $in[0]);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
_set(target, prependKey, $in);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
_set(target, prependKey, sourceVal);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (typeOfTargetVal !== "object" || typeOfSourceVal !== "object") {
|
|
204
|
+
_set(target, prependKey, sourceVal);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
// both are objects
|
|
208
|
+
const sourceKeys = Object.keys(sourceVal);
|
|
209
|
+
for (let i = 0, n = sourceKeys.length; i < n; i++) {
|
|
210
|
+
const key = sourceKeys[i];
|
|
211
|
+
handleCircular(target, source, [...prependKey, key], options);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function makeDefaultOptions(options) {
|
|
215
|
+
options = options || {};
|
|
216
|
+
options.defaultHandle = options.defaultHandle || "combine";
|
|
217
|
+
options.useLogicalConjunction =
|
|
218
|
+
(Object.prototype.hasOwnProperty.call(options, "useLogicalConjunction"))
|
|
219
|
+
? options.useLogicalConjunction
|
|
220
|
+
: false;
|
|
221
|
+
options.actionOnEmptyIntersect = options.actionOnEmptyIntersect || (() => {
|
|
222
|
+
throw new Forbidden("You're not allowed to make this request");
|
|
223
|
+
});
|
|
224
|
+
options.handle = options.handle || {};
|
|
225
|
+
if (options.defaultHandle === "intersect") {
|
|
226
|
+
options.handle.$select = options.handle.$select || "intersectOrFull";
|
|
227
|
+
}
|
|
228
|
+
return options;
|
|
229
|
+
}
|
|
230
|
+
export function mergeQuery(target, source, options) {
|
|
231
|
+
const fullOptions = makeDefaultOptions(options);
|
|
232
|
+
const { filters: targetFilters, query: targetQuery } = filterQuery(target, {
|
|
233
|
+
operators: fullOptions.operators,
|
|
234
|
+
service: fullOptions.service
|
|
235
|
+
});
|
|
236
|
+
if (target.$limit) {
|
|
237
|
+
targetFilters.$limit = target.$limit;
|
|
238
|
+
}
|
|
239
|
+
let {
|
|
240
|
+
// eslint-disable-next-line prefer-const
|
|
241
|
+
filters: sourceFilters, query: sourceQuery } = filterQuery(source, {
|
|
242
|
+
operators: fullOptions.operators,
|
|
243
|
+
service: fullOptions.service
|
|
244
|
+
});
|
|
245
|
+
if (source.$limit) {
|
|
246
|
+
sourceFilters.$limit = source.$limit;
|
|
247
|
+
}
|
|
248
|
+
//#region filters
|
|
249
|
+
if (target &&
|
|
250
|
+
!Object.prototype.hasOwnProperty.call(target, "$limit") &&
|
|
251
|
+
Object.prototype.hasOwnProperty.call(targetFilters, "$limit")) {
|
|
252
|
+
delete targetFilters.$limit;
|
|
253
|
+
}
|
|
254
|
+
if (source &&
|
|
255
|
+
!Object.prototype.hasOwnProperty.call(source, "$limit") &&
|
|
256
|
+
Object.prototype.hasOwnProperty.call(sourceFilters, "$limit")) {
|
|
257
|
+
delete sourceFilters.$limit;
|
|
258
|
+
}
|
|
259
|
+
handleArray(targetFilters, sourceFilters, ["$select"], fullOptions);
|
|
260
|
+
// remaining filters
|
|
261
|
+
delete sourceFilters["$select"];
|
|
262
|
+
_merge(targetFilters, sourceFilters);
|
|
263
|
+
//#endregion
|
|
264
|
+
//#region '$or' / '$and'
|
|
265
|
+
if (options?.useLogicalConjunction &&
|
|
266
|
+
(options.defaultHandle === "combine" ||
|
|
267
|
+
options.defaultHandle === "intersect") &&
|
|
268
|
+
!_isEmpty(targetQuery)) {
|
|
269
|
+
const logicalOp = (options.defaultHandle === "combine")
|
|
270
|
+
? "$or"
|
|
271
|
+
: "$and";
|
|
272
|
+
if (Object.prototype.hasOwnProperty.call(sourceQuery, logicalOp)) {
|
|
273
|
+
// omit '$or'/'$and' and put all other props into '$or'/'$and'
|
|
274
|
+
const andOr = sourceQuery[logicalOp];
|
|
275
|
+
delete sourceQuery[logicalOp];
|
|
276
|
+
andOr.push(sourceQuery);
|
|
277
|
+
sourceQuery = { [logicalOp]: andOr };
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
sourceQuery = { [logicalOp]: [sourceQuery] };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
//#endregion
|
|
284
|
+
const keys = Object.keys(sourceQuery);
|
|
285
|
+
for (let i = 0, n = keys.length; i < n; i++) {
|
|
286
|
+
const key = keys[i];
|
|
287
|
+
handleCircular(targetQuery, sourceQuery, [key], fullOptions);
|
|
288
|
+
}
|
|
289
|
+
const result = Object.assign({}, targetFilters, targetQuery);
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
function getParentProp(target, path) {
|
|
293
|
+
if (path.length <= 1) {
|
|
294
|
+
return target;
|
|
295
|
+
}
|
|
296
|
+
const pathOneUp = path.slice(0, -1);
|
|
297
|
+
return _get(target, pathOneUp);
|
|
298
|
+
}
|
|
299
|
+
function cleanOr(target) {
|
|
300
|
+
if (!target || !Array.isArray(target) || target.length <= 0) {
|
|
301
|
+
return target;
|
|
302
|
+
}
|
|
303
|
+
if (target.some(x => _isEmpty(x))) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
return arrayWithoutDuplicates(target);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function cleanAnd(target) {
|
|
311
|
+
if (!target || !Array.isArray(target) || target.length <= 0) {
|
|
312
|
+
return target;
|
|
313
|
+
}
|
|
314
|
+
if (target.every(x => _isEmpty(x))) {
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
target = target.filter(x => !_isEmpty(x));
|
|
319
|
+
return arrayWithoutDuplicates(target);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
function arrayWithoutDuplicates(target) {
|
|
323
|
+
if (!target || !Array.isArray(target)) {
|
|
324
|
+
return target;
|
|
325
|
+
}
|
|
326
|
+
return _uniqWith(target, _isEqual);
|
|
327
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export function mergeArrays(targetArr, sourceArr, handle, prependKey, actionOnEmptyIntersect) {
|
|
2
|
+
if (!sourceArr && !targetArr) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
if (handle === "target") {
|
|
6
|
+
return targetArr;
|
|
7
|
+
}
|
|
8
|
+
else if (handle === "source") {
|
|
9
|
+
return sourceArr;
|
|
10
|
+
}
|
|
11
|
+
else if (handle === "combine") {
|
|
12
|
+
if (!sourceArr || !Array.isArray(sourceArr)) {
|
|
13
|
+
return targetArr;
|
|
14
|
+
}
|
|
15
|
+
if (!targetArr || !Array.isArray(targetArr)) {
|
|
16
|
+
return sourceArr;
|
|
17
|
+
}
|
|
18
|
+
const arr = targetArr.concat(sourceArr);
|
|
19
|
+
return [...new Set(arr)];
|
|
20
|
+
}
|
|
21
|
+
else if (handle === "intersect" || handle === "intersectOrFull") {
|
|
22
|
+
const targetIsArray = !targetArr || !Array.isArray(targetArr);
|
|
23
|
+
const sourceIsArray = !sourceArr || !Array.isArray(sourceArr);
|
|
24
|
+
if ((targetIsArray || sourceIsArray) && handle === "intersect") {
|
|
25
|
+
if (actionOnEmptyIntersect) {
|
|
26
|
+
actionOnEmptyIntersect(targetArr, sourceArr, prependKey || []);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (handle === "intersectOrFull") {
|
|
31
|
+
const val = (!targetIsArray) ? targetArr : sourceArr;
|
|
32
|
+
return val;
|
|
33
|
+
}
|
|
34
|
+
return targetArr.filter(val => sourceArr.includes(val));
|
|
35
|
+
}
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import _isEqual from "lodash/isEqual";
|
|
2
|
+
import _get from "lodash/get";
|
|
3
|
+
import _set from "lodash/set";
|
|
4
|
+
export const pushSet = (obj, path, val, options) => {
|
|
5
|
+
options = options || {};
|
|
6
|
+
let arr = _get(obj, path);
|
|
7
|
+
if (!arr || !Array.isArray(arr)) {
|
|
8
|
+
arr = [val];
|
|
9
|
+
_set(obj, path, arr);
|
|
10
|
+
return arr;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
if (options.unique && arr.some(x => _isEqual(x, val))) {
|
|
14
|
+
return arr;
|
|
15
|
+
}
|
|
16
|
+
arr.push(val);
|
|
17
|
+
return arr;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { isMulti } from "..";
|
|
2
|
+
import { isPaginated } from "./isPaginated";
|
|
3
|
+
export const setResultEmpty = (context) => {
|
|
4
|
+
if (context.result) {
|
|
5
|
+
return context;
|
|
6
|
+
}
|
|
7
|
+
const multi = isMulti(context);
|
|
8
|
+
if (multi) {
|
|
9
|
+
if (context.method === "find" && isPaginated(context)) {
|
|
10
|
+
context.result = {
|
|
11
|
+
total: 0,
|
|
12
|
+
skip: 0,
|
|
13
|
+
limit: 0,
|
|
14
|
+
data: []
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
context.result = [];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
context.result = null;
|
|
23
|
+
}
|
|
24
|
+
return context;
|
|
25
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Kudos to @DaddyWarbucks! This is a cheeky copy of his awesome library: 'feathers-fletching'.
|
|
2
|
+
// Definitely check it out! https://daddywarbucks.github.io/feathers-fletching/overview.html
|
|
3
|
+
import { GeneralError } from "@feathersjs/errors";
|
|
4
|
+
export const shouldSkip = (hookName, context) => {
|
|
5
|
+
if (!context.params || !context.params.skipHooks) {
|
|
6
|
+
return false;
|
|
7
|
+
}
|
|
8
|
+
const { skipHooks } = context.params;
|
|
9
|
+
if (!Array.isArray(skipHooks)) {
|
|
10
|
+
throw new GeneralError("The `skipHooks` param must be an Array of Strings");
|
|
11
|
+
}
|
|
12
|
+
const { type } = context;
|
|
13
|
+
if (skipHooks.includes(hookName)) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (skipHooks.includes("all")) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (type === "before") {
|
|
20
|
+
return (skipHooks.includes(`before:${hookName}`) || skipHooks.includes("before"));
|
|
21
|
+
}
|
|
22
|
+
if (type === "after") {
|
|
23
|
+
return (skipHooks.includes(`after:${hookName}`) || skipHooks.includes("after"));
|
|
24
|
+
}
|
|
25
|
+
if (type === "error") {
|
|
26
|
+
return (skipHooks.includes(`error:${hookName}`) || skipHooks.includes("error"));
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
};
|
package/dist/hooks/checkMulti.js
CHANGED
|
@@ -6,11 +6,11 @@ const shouldSkip_1 = require("../utils/shouldSkip");
|
|
|
6
6
|
const isMulti_1 = require("../utils/isMulti");
|
|
7
7
|
function checkMulti() {
|
|
8
8
|
return (context) => {
|
|
9
|
-
if (shouldSkip_1.shouldSkip("checkMulti", context)) {
|
|
9
|
+
if ((0, shouldSkip_1.shouldSkip)("checkMulti", context)) {
|
|
10
10
|
return context;
|
|
11
11
|
}
|
|
12
12
|
const { service, method } = context;
|
|
13
|
-
if (!service.allowsMulti || !isMulti_1.isMulti(context) || method === "find") {
|
|
13
|
+
if (!service.allowsMulti || !(0, isMulti_1.isMulti)(context) || method === "find") {
|
|
14
14
|
return context;
|
|
15
15
|
}
|
|
16
16
|
if (!service.allowsMulti(method)) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import type { HookRunPerItemOptions } from "../types";
|
|
2
2
|
import type { HookContext } from "@feathersjs/feathers";
|
|
3
|
-
|
|
3
|
+
import type { Promisable } from "type-fest";
|
|
4
|
+
export declare const runPerItem: (actionPerItem: (item: any, context: HookContext) => Promisable<any>, options: HookRunPerItemOptions) => (context: HookContext) => Promise<HookContext>;
|
package/dist/hooks/runPerItem.js
CHANGED
|
@@ -21,10 +21,11 @@ const makeOptions = (options) => {
|
|
|
21
21
|
const runPerItem = (actionPerItem, options) => {
|
|
22
22
|
options = makeOptions(options);
|
|
23
23
|
return (context) => __awaiter(void 0, void 0, void 0, function* () {
|
|
24
|
-
if (shouldSkip_1.shouldSkip("runForItems", context)) {
|
|
24
|
+
if ((0, shouldSkip_1.shouldSkip)("runForItems", context)) {
|
|
25
25
|
return context;
|
|
26
26
|
}
|
|
27
|
-
|
|
27
|
+
//@ts-expect-error type error because feathers-hooks-common is feathers@4
|
|
28
|
+
let items = (0, feathers_hooks_common_1.getItems)(context);
|
|
28
29
|
items = (Array.isArray(items)) ? items : [items];
|
|
29
30
|
const promises = items.map((item) => __awaiter(void 0, void 0, void 0, function* () {
|
|
30
31
|
yield actionPerItem(item, context);
|
package/dist/hooks/setData.js
CHANGED
|
@@ -17,23 +17,24 @@ function setData(from, to, _options) {
|
|
|
17
17
|
const options = Object.assign({}, defaultOptions, _options);
|
|
18
18
|
return (context) => {
|
|
19
19
|
var _a;
|
|
20
|
-
|
|
20
|
+
//@ts-expect-error type error because feathers-hooks-common is feathers@4
|
|
21
|
+
let items = (0, feathers_hooks_common_1.getItems)(context);
|
|
21
22
|
items = (Array.isArray(items)) ? items : [items];
|
|
22
|
-
if (!has_1.default(context, from)) {
|
|
23
|
+
if (!(0, has_1.default)(context, from)) {
|
|
23
24
|
if (!((_a = context.params) === null || _a === void 0 ? void 0 : _a.provider) || options.allowUndefined === true) {
|
|
24
25
|
return context;
|
|
25
26
|
}
|
|
26
|
-
if (!options.overwrite && items.every((item) => has_1.default(item, to))) {
|
|
27
|
+
if (!options.overwrite && items.every((item) => (0, has_1.default)(item, to))) {
|
|
27
28
|
return context;
|
|
28
29
|
}
|
|
29
30
|
throw new errors_1.Forbidden(`Expected field ${from.toString()} not available`);
|
|
30
31
|
}
|
|
31
|
-
const val = get_1.default(context, from);
|
|
32
|
+
const val = (0, get_1.default)(context, from);
|
|
32
33
|
items.forEach((item) => {
|
|
33
|
-
if (!options.overwrite && has_1.default(item, to)) {
|
|
34
|
+
if (!options.overwrite && (0, has_1.default)(item, to)) {
|
|
34
35
|
return;
|
|
35
36
|
}
|
|
36
|
-
set_1.default(item, to, val);
|
|
37
|
+
(0, set_1.default)(item, to, val);
|
|
37
38
|
});
|
|
38
39
|
return context;
|
|
39
40
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -4,23 +4,26 @@ import { runPerItem } from "./hooks/runPerItem";
|
|
|
4
4
|
export declare const hooks: {
|
|
5
5
|
checkMulti: typeof checkMulti;
|
|
6
6
|
setData: typeof setData;
|
|
7
|
-
runPerItem: (actionPerItem: (item:
|
|
7
|
+
runPerItem: (actionPerItem: (item: any, context: import("@feathersjs/feathers/lib").HookContext<import("@feathersjs/feathers/lib").Application<any, any>, any>) => any, options: import("./types").HookRunPerItemOptions) => (context: import("@feathersjs/feathers/lib").HookContext<import("@feathersjs/feathers/lib").Application<any, any>, any>) => Promise<import("@feathersjs/feathers/lib").HookContext<import("@feathersjs/feathers/lib").Application<any, any>, any>>;
|
|
8
8
|
};
|
|
9
9
|
export { checkMulti };
|
|
10
10
|
export { setData };
|
|
11
11
|
export { runPerItem };
|
|
12
|
-
import { debounceMixin, DebouncedStore } from "./mixins/debounce-mixin";
|
|
12
|
+
import { debounceMixin, DebouncedService, DebouncedStore } from "./mixins/debounce-mixin";
|
|
13
13
|
export declare const mixins: {
|
|
14
14
|
debounceMixin: typeof debounceMixin;
|
|
15
15
|
DebouncedStore: typeof DebouncedStore;
|
|
16
16
|
};
|
|
17
17
|
export { debounceMixin };
|
|
18
|
+
export { DebouncedService };
|
|
18
19
|
export { DebouncedStore };
|
|
19
|
-
export {
|
|
20
|
+
export { getPaginate } from "./utils/getPaginate";
|
|
20
21
|
export { isMulti } from "./utils/isMulti";
|
|
22
|
+
export { isPaginated } from "./utils/isPaginated";
|
|
21
23
|
export { mergeQuery } from "./utils/mergeQuery/index";
|
|
22
24
|
export { mergeArrays } from "./utils/mergeQuery/mergeArrays";
|
|
23
25
|
export { pushSet } from "./utils/pushSet";
|
|
26
|
+
export { setResultEmpty } from "./utils/setResultEmpty";
|
|
24
27
|
export { markHookForSkip } from "./utils/markHookForSkip";
|
|
25
28
|
export { shouldSkip } from "./utils/shouldSkip";
|
|
26
29
|
export { filterQuery } from "./utils/filterQuery";
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
10
10
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
11
|
};
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
-
exports.filterQuery = exports.shouldSkip = exports.markHookForSkip = exports.pushSet = exports.mergeArrays = exports.mergeQuery = exports.isMulti = exports.
|
|
13
|
+
exports.filterQuery = exports.shouldSkip = exports.markHookForSkip = exports.setResultEmpty = exports.pushSet = exports.mergeArrays = exports.mergeQuery = exports.isPaginated = exports.isMulti = exports.getPaginate = exports.DebouncedStore = exports.debounceMixin = exports.mixins = exports.runPerItem = exports.setData = exports.checkMulti = exports.hooks = void 0;
|
|
14
14
|
// hooks
|
|
15
15
|
const checkMulti_1 = require("./hooks/checkMulti");
|
|
16
16
|
Object.defineProperty(exports, "checkMulti", { enumerable: true, get: function () { return checkMulti_1.checkMulti; } });
|
|
@@ -30,16 +30,20 @@ exports.mixins = {
|
|
|
30
30
|
debounceMixin: debounce_mixin_1.debounceMixin,
|
|
31
31
|
DebouncedStore: debounce_mixin_1.DebouncedStore
|
|
32
32
|
};
|
|
33
|
-
var
|
|
34
|
-
Object.defineProperty(exports, "
|
|
33
|
+
var getPaginate_1 = require("./utils/getPaginate");
|
|
34
|
+
Object.defineProperty(exports, "getPaginate", { enumerable: true, get: function () { return getPaginate_1.getPaginate; } });
|
|
35
35
|
var isMulti_1 = require("./utils/isMulti");
|
|
36
36
|
Object.defineProperty(exports, "isMulti", { enumerable: true, get: function () { return isMulti_1.isMulti; } });
|
|
37
|
+
var isPaginated_1 = require("./utils/isPaginated");
|
|
38
|
+
Object.defineProperty(exports, "isPaginated", { enumerable: true, get: function () { return isPaginated_1.isPaginated; } });
|
|
37
39
|
var index_1 = require("./utils/mergeQuery/index");
|
|
38
40
|
Object.defineProperty(exports, "mergeQuery", { enumerable: true, get: function () { return index_1.mergeQuery; } });
|
|
39
41
|
var mergeArrays_1 = require("./utils/mergeQuery/mergeArrays");
|
|
40
42
|
Object.defineProperty(exports, "mergeArrays", { enumerable: true, get: function () { return mergeArrays_1.mergeArrays; } });
|
|
41
43
|
var pushSet_1 = require("./utils/pushSet");
|
|
42
44
|
Object.defineProperty(exports, "pushSet", { enumerable: true, get: function () { return pushSet_1.pushSet; } });
|
|
45
|
+
var setResultEmpty_1 = require("./utils/setResultEmpty");
|
|
46
|
+
Object.defineProperty(exports, "setResultEmpty", { enumerable: true, get: function () { return setResultEmpty_1.setResultEmpty; } });
|
|
43
47
|
var markHookForSkip_1 = require("./utils/markHookForSkip");
|
|
44
48
|
Object.defineProperty(exports, "markHookForSkip", { enumerable: true, get: function () { return markHookForSkip_1.markHookForSkip; } });
|
|
45
49
|
var shouldSkip_1 = require("./utils/shouldSkip");
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { DebouncedFunc } from "lodash";
|
|
2
2
|
import type { Application, Id } from "@feathersjs/feathers";
|
|
3
|
-
import { DebouncedFunctionApp, DebouncedStoreOptions } from "../../types";
|
|
3
|
+
import type { DebouncedFunctionApp, DebouncedStoreOptions } from "../../types";
|
|
4
4
|
export declare const makeDefaultOptions: () => DebouncedStoreOptions;
|
|
5
5
|
export declare class DebouncedStore {
|
|
6
6
|
private _app;
|
|
7
7
|
private _options;
|
|
8
8
|
private _isRunningById;
|
|
9
9
|
_queueById: Record<string, DebouncedFunc<((id: Id, action: DebouncedFunctionApp) => void | Promise<void>)>>;
|
|
10
|
-
add: (id: Id, action: (app?: Application<
|
|
10
|
+
add: (id: Id, action: (app?: Application<any, any> | undefined) => void | Promise<void>) => void | Promise<void> | undefined;
|
|
11
11
|
constructor(app: Application, options?: Partial<DebouncedStoreOptions>);
|
|
12
12
|
private unbounced;
|
|
13
13
|
private debounceById;
|
|
@@ -26,7 +26,7 @@ exports.makeDefaultOptions = makeDefaultOptions;
|
|
|
26
26
|
class DebouncedStore {
|
|
27
27
|
constructor(app, options) {
|
|
28
28
|
this._app = app;
|
|
29
|
-
this._options = Object.assign(exports.makeDefaultOptions(), options);
|
|
29
|
+
this._options = Object.assign((0, exports.makeDefaultOptions)(), options);
|
|
30
30
|
this._queueById = {};
|
|
31
31
|
this._isRunningById = {};
|
|
32
32
|
//this._waitingById = {};
|
|
@@ -52,7 +52,7 @@ class DebouncedStore {
|
|
|
52
52
|
if (typeof this._queueById[id] === "function") {
|
|
53
53
|
return this._queueById[id](id, action);
|
|
54
54
|
}
|
|
55
|
-
this._queueById[id] = debounce_1.default((id, action) => {
|
|
55
|
+
this._queueById[id] = (0, debounce_1.default)((id, action) => {
|
|
56
56
|
this.unbounced(id, action);
|
|
57
57
|
}, wait, Object.assign(Object.assign({}, options), { leading: false })); // leading required for return promise
|
|
58
58
|
return this._queueById[id](id, action);
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { DebouncedStore } from "./DebouncedStore";
|
|
2
|
-
import type { Application } from "@feathersjs/feathers";
|
|
2
|
+
import type { Application, FeathersService } from "@feathersjs/feathers";
|
|
3
3
|
import type { InitDebounceMixinOptions } from "../../types";
|
|
4
|
+
export declare type DebouncedService = FeathersService & {
|
|
5
|
+
debouncedStore?: DebouncedStore;
|
|
6
|
+
};
|
|
4
7
|
export declare function debounceMixin(options?: Partial<InitDebounceMixinOptions>): ((app: Application) => void);
|
|
5
8
|
export { DebouncedStore };
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "DebouncedStore", { enumerable: true, get: functi
|
|
|
6
6
|
function debounceMixin(options) {
|
|
7
7
|
return (app) => {
|
|
8
8
|
options = options || {};
|
|
9
|
-
const defaultOptions = Object.assign(DebouncedStore_1.makeDefaultOptions(), options === null || options === void 0 ? void 0 : options.default);
|
|
9
|
+
const defaultOptions = Object.assign((0, DebouncedStore_1.makeDefaultOptions)(), options === null || options === void 0 ? void 0 : options.default);
|
|
10
10
|
app.mixins.push((service, path) => {
|
|
11
11
|
// if path is on blacklist, don't add debouncedStore to service
|
|
12
12
|
if ((options === null || options === void 0 ? void 0 : options.blacklist) && options.blacklist.includes(path))
|