feathers-utils 2.0.0-0 → 2.0.0-2

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 (97) hide show
  1. package/README.md +16 -9
  2. package/dist/esm/filters/array.d.ts +2 -0
  3. package/dist/esm/filters/array.js +10 -0
  4. package/dist/esm/hooks/checkMulti.d.ts +2 -2
  5. package/dist/esm/hooks/createRelated.d.ts +3 -0
  6. package/dist/esm/hooks/createRelated.js +26 -0
  7. package/dist/esm/hooks/onDelete.d.ts +3 -0
  8. package/dist/esm/hooks/onDelete.js +40 -0
  9. package/dist/esm/hooks/removeRelated.d.ts +3 -0
  10. package/dist/esm/hooks/removeRelated.js +30 -0
  11. package/dist/esm/hooks/runPerItem.d.ts +2 -3
  12. package/dist/esm/hooks/runPerItem.js +6 -6
  13. package/dist/esm/hooks/setData.d.ts +2 -3
  14. package/dist/esm/hooks/setData.js +13 -8
  15. package/dist/esm/index.d.ts +8 -2
  16. package/dist/esm/index.js +8 -1
  17. package/dist/esm/mixins/debounce-mixin/DebouncedStore.d.ts +1 -1
  18. package/dist/esm/mixins/debounce-mixin/DebouncedStore.js +1 -1
  19. package/dist/esm/mixins/debounce-mixin/index.d.ts +1 -1
  20. package/dist/esm/types.d.ts +35 -16
  21. package/dist/esm/utils/filterQuery.d.ts +2 -2
  22. package/dist/esm/utils/filterQuery.js +9 -7
  23. package/dist/esm/utils/getItemsIsArray.d.ts +3 -0
  24. package/dist/esm/utils/getItemsIsArray.js +18 -0
  25. package/dist/esm/utils/getPaginate.d.ts +1 -1
  26. package/dist/esm/utils/isPaginated.d.ts +1 -1
  27. package/dist/esm/utils/markHookForSkip.d.ts +2 -2
  28. package/dist/esm/utils/mergeQuery/index.js +18 -7
  29. package/dist/esm/utils/pushSet.d.ts +1 -1
  30. package/dist/esm/utils/pushSet.js +3 -3
  31. package/dist/esm/utils/setResultEmpty.d.ts +1 -1
  32. package/dist/esm/utils/validateQueryProperty.d.ts +2 -0
  33. package/dist/esm/utils/validateQueryProperty.js +20 -0
  34. package/dist/filters/array.d.ts +2 -0
  35. package/dist/filters/array.js +14 -0
  36. package/dist/hooks/checkMulti.d.ts +2 -2
  37. package/dist/hooks/createRelated.d.ts +3 -0
  38. package/dist/hooks/createRelated.js +39 -0
  39. package/dist/hooks/onDelete.d.ts +3 -0
  40. package/dist/hooks/onDelete.js +53 -0
  41. package/dist/hooks/removeRelated.d.ts +3 -0
  42. package/dist/hooks/removeRelated.js +43 -0
  43. package/dist/hooks/runPerItem.d.ts +2 -3
  44. package/dist/hooks/runPerItem.js +6 -6
  45. package/dist/hooks/setData.d.ts +2 -3
  46. package/dist/hooks/setData.js +17 -12
  47. package/dist/index.d.ts +8 -2
  48. package/dist/index.js +21 -4
  49. package/dist/mixins/debounce-mixin/DebouncedStore.d.ts +1 -1
  50. package/dist/mixins/debounce-mixin/DebouncedStore.js +2 -2
  51. package/dist/mixins/debounce-mixin/index.d.ts +1 -1
  52. package/dist/types.d.ts +35 -16
  53. package/dist/utils/filterQuery.d.ts +2 -2
  54. package/dist/utils/filterQuery.js +19 -6
  55. package/dist/utils/getItemsIsArray.d.ts +3 -0
  56. package/dist/utils/getItemsIsArray.js +22 -0
  57. package/dist/utils/getPaginate.d.ts +1 -1
  58. package/dist/utils/isPaginated.d.ts +1 -1
  59. package/dist/utils/markHookForSkip.d.ts +2 -2
  60. package/dist/utils/mergeQuery/index.js +53 -42
  61. package/dist/utils/pushSet.d.ts +1 -1
  62. package/dist/utils/pushSet.js +6 -6
  63. package/dist/utils/setResultEmpty.d.ts +1 -1
  64. package/dist/utils/validateQueryProperty.d.ts +2 -0
  65. package/dist/utils/validateQueryProperty.js +22 -0
  66. package/package.json +33 -19
  67. package/src/filters/array.ts +14 -0
  68. package/src/hooks/checkMulti.ts +3 -1
  69. package/src/hooks/createRelated.ts +45 -0
  70. package/src/hooks/onDelete.ts +56 -0
  71. package/src/hooks/removeRelated.ts +42 -0
  72. package/src/hooks/runPerItem.ts +9 -12
  73. package/src/hooks/setData.ts +17 -12
  74. package/src/index.ts +11 -1
  75. package/src/mixins/debounce-mixin/DebouncedStore.ts +49 -49
  76. package/src/mixins/debounce-mixin/index.ts +6 -3
  77. package/src/types.ts +46 -16
  78. package/src/utils/filterQuery.ts +15 -14
  79. package/src/utils/getItemsIsArray.ts +23 -0
  80. package/src/utils/getPaginate.ts +1 -2
  81. package/src/utils/isMulti.ts +3 -1
  82. package/src/utils/isPaginated.ts +1 -1
  83. package/src/utils/markHookForSkip.ts +2 -2
  84. package/src/utils/mergeQuery/index.ts +20 -8
  85. package/src/utils/pushSet.ts +3 -3
  86. package/src/utils/setResultEmpty.ts +1 -1
  87. package/src/utils/shouldSkip.ts +4 -1
  88. package/src/utils/validateQueryProperty.ts +27 -0
  89. package/.eslintignore +0 -3
  90. package/.eslintrc.js +0 -44
  91. package/.gitlab-ci.yml +0 -11
  92. package/.mocharc.js +0 -11
  93. package/.nycrc.json +0 -22
  94. package/index.js +0 -9
  95. package/tsconfig-esm.json +0 -9
  96. package/tsconfig.json +0 -16
  97. package/tsconfig.test.json +0 -13
@@ -4,13 +4,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.mergeQuery = void 0;
7
- const get_1 = __importDefault(require("lodash/get"));
8
- const has_1 = __importDefault(require("lodash/has"));
9
- const isEmpty_1 = __importDefault(require("lodash/isEmpty"));
10
- const isEqual_1 = __importDefault(require("lodash/isEqual"));
11
- const merge_1 = __importDefault(require("lodash/merge"));
12
- const set_1 = __importDefault(require("lodash/set"));
13
- const uniqWith_1 = __importDefault(require("lodash/uniqWith"));
7
+ const get_js_1 = __importDefault(require("lodash/get.js"));
8
+ const has_js_1 = __importDefault(require("lodash/has.js"));
9
+ const isEmpty_js_1 = __importDefault(require("lodash/isEmpty.js"));
10
+ const isEqual_js_1 = __importDefault(require("lodash/isEqual.js"));
11
+ const merge_js_1 = __importDefault(require("lodash/merge.js"));
12
+ const set_js_1 = __importDefault(require("lodash/set.js"));
13
+ const uniqWith_js_1 = __importDefault(require("lodash/uniqWith.js"));
14
14
  const mergeArrays_1 = require("./mergeArrays");
15
15
  const filterQuery_1 = require("../filterQuery");
16
16
  const errors_1 = require("@feathersjs/errors");
@@ -18,14 +18,14 @@ const hasOwnProperty = (obj, key) => {
18
18
  return Object.prototype.hasOwnProperty.call(obj, key);
19
19
  };
20
20
  function handleArray(target, source, key, options) {
21
- const targetVal = (0, get_1.default)(target, key);
22
- const sourceVal = (0, get_1.default)(source, key);
21
+ const targetVal = (0, get_js_1.default)(target, key);
22
+ const sourceVal = (0, get_js_1.default)(source, key);
23
23
  if (!sourceVal && !targetVal) {
24
24
  return;
25
25
  }
26
- const handle = (0, get_1.default)(options, ["handle", ...key], options.defaultHandle);
26
+ const handle = (0, get_js_1.default)(options, ["handle", ...key], options.defaultHandle);
27
27
  const arr = (0, mergeArrays_1.mergeArrays)(targetVal, sourceVal, handle, key, options.actionOnEmptyIntersect);
28
- (0, set_1.default)(target, key, arr);
28
+ (0, set_js_1.default)(target, key, arr);
29
29
  }
30
30
  function handleCircular(target, source, prependKey, options) {
31
31
  if (target === null || target === void 0 ? void 0 : target.$or) {
@@ -52,11 +52,11 @@ function handleCircular(target, source, prependKey, options) {
52
52
  delete source.$and;
53
53
  }
54
54
  }
55
- if (!(0, has_1.default)(source, prependKey)) {
55
+ if (!(0, has_js_1.default)(source, prependKey)) {
56
56
  return;
57
57
  }
58
- if (!(0, has_1.default)(target, prependKey)) {
59
- (0, set_1.default)(target, prependKey, (0, get_1.default)(source, prependKey));
58
+ if (!(0, has_js_1.default)(target, prependKey)) {
59
+ (0, set_js_1.default)(target, prependKey, (0, get_js_1.default)(source, prependKey));
60
60
  return;
61
61
  }
62
62
  const { defaultHandle, actionOnEmptyIntersect } = options;
@@ -64,22 +64,22 @@ function handleCircular(target, source, prependKey, options) {
64
64
  return;
65
65
  }
66
66
  const getTargetVal = () => {
67
- return (prependKey.length > 0) ? (0, get_1.default)(target, prependKey) : target;
67
+ return (prependKey.length > 0) ? (0, get_js_1.default)(target, prependKey) : target;
68
68
  };
69
69
  const getSourceVal = () => {
70
- return (prependKey.length > 0) ? (0, get_1.default)(source, prependKey) : source;
70
+ return (prependKey.length > 0) ? (0, get_js_1.default)(source, prependKey) : source;
71
71
  };
72
72
  const targetVal = getTargetVal();
73
73
  const sourceVal = getSourceVal();
74
- if ((0, isEqual_1.default)(targetVal, sourceVal)) {
74
+ if ((0, isEqual_js_1.default)(targetVal, sourceVal)) {
75
75
  return;
76
76
  }
77
77
  if (defaultHandle === "source") {
78
- (0, set_1.default)(target, prependKey, sourceVal);
78
+ (0, set_js_1.default)(target, prependKey, sourceVal);
79
79
  return;
80
80
  }
81
81
  if (targetVal === null || sourceVal === null) {
82
- (0, set_1.default)(target, prependKey, sourceVal);
82
+ (0, set_js_1.default)(target, prependKey, sourceVal);
83
83
  return;
84
84
  }
85
85
  const typeOfTargetVal = typeof targetVal;
@@ -87,7 +87,7 @@ function handleCircular(target, source, prependKey, options) {
87
87
  if (defaultHandle === "intersect") {
88
88
  actionOnEmptyIntersect(target, source, prependKey);
89
89
  }
90
- (0, set_1.default)(target, prependKey, sourceVal);
90
+ (0, set_js_1.default)(target, prependKey, sourceVal);
91
91
  return;
92
92
  }
93
93
  const typeOfSourceVal = typeof sourceVal;
@@ -96,7 +96,7 @@ function handleCircular(target, source, prependKey, options) {
96
96
  if (isTargetSimple || isSourceSimple) {
97
97
  if (isTargetSimple && isSourceSimple) {
98
98
  if (defaultHandle === "combine") {
99
- (0, set_1.default)(target, prependKey, { $in: [...new Set([targetVal, sourceVal])] });
99
+ (0, set_js_1.default)(target, prependKey, { $in: [...new Set([targetVal, sourceVal])] });
100
100
  return;
101
101
  }
102
102
  else if (defaultHandle === "intersect") {
@@ -110,20 +110,20 @@ function handleCircular(target, source, prependKey, options) {
110
110
  const targetHasIn = hasOwnProperty(targetVal, "$in");
111
111
  const $in = (targetHasIn) ? targetVal["$in"] : sourceVal["$in"];
112
112
  const otherVal = (isTargetSimple) ? targetVal : sourceVal;
113
- if ($in.length === 1 && (0, isEqual_1.default)($in[0], otherVal)) {
114
- (0, set_1.default)(target, prependKey, otherVal);
113
+ if ($in.length === 1 && (0, isEqual_js_1.default)($in[0], otherVal)) {
114
+ (0, set_js_1.default)(target, prependKey, otherVal);
115
115
  return;
116
116
  }
117
117
  else if (defaultHandle === "combine") {
118
- if (!$in.some((x) => (0, isEqual_1.default)(x, otherVal))) {
118
+ if (!$in.some((x) => (0, isEqual_js_1.default)(x, otherVal))) {
119
119
  $in.push(otherVal);
120
120
  }
121
- (0, set_1.default)(target, `${prependKey}.$in`, $in);
121
+ (0, set_js_1.default)(target, `${prependKey}.$in`, $in);
122
122
  return;
123
123
  }
124
124
  else if (defaultHandle === "intersect") {
125
- if ($in.some((x) => (0, isEqual_1.default)(x, otherVal))) {
126
- (0, set_1.default)(target, prependKey, otherVal);
125
+ if ($in.some((x) => (0, isEqual_js_1.default)(x, otherVal))) {
126
+ (0, set_js_1.default)(target, prependKey, otherVal);
127
127
  }
128
128
  else {
129
129
  actionOnEmptyIntersect(target, source, prependKey);
@@ -139,7 +139,7 @@ function handleCircular(target, source, prependKey, options) {
139
139
  const key = prependKey[prependKey.length - 1];
140
140
  if (key === "$or") {
141
141
  if (defaultHandle === "combine") {
142
- const newVals = sourceVal.filter((x) => !targetVal.some((y) => (0, isEqual_1.default)(x, y)));
142
+ const newVals = sourceVal.filter((x) => !targetVal.some((y) => (0, isEqual_js_1.default)(x, y)));
143
143
  targetVal.push(...newVals);
144
144
  }
145
145
  else if (defaultHandle === "intersect") {
@@ -176,7 +176,7 @@ function handleCircular(target, source, prependKey, options) {
176
176
  return;
177
177
  }
178
178
  else if (defaultHandle === "intersect") {
179
- const newVals = sourceVal.filter((x) => !targetVal.some((y) => (0, isEqual_1.default)(x, y)));
179
+ const newVals = sourceVal.filter((x) => !targetVal.some((y) => (0, isEqual_js_1.default)(x, y)));
180
180
  targetVal.push(...newVals);
181
181
  return;
182
182
  }
@@ -185,29 +185,29 @@ function handleCircular(target, source, prependKey, options) {
185
185
  if (defaultHandle === "combine") {
186
186
  let $in = targetVal.concat(sourceVal);
187
187
  $in = [...new Set($in)];
188
- (0, set_1.default)(target, prependKey, $in);
188
+ (0, set_js_1.default)(target, prependKey, $in);
189
189
  return;
190
190
  }
191
191
  else if (defaultHandle === "intersect") {
192
- const $in = targetVal.filter((x) => sourceVal.some((y) => (0, isEqual_1.default)(x, y)));
192
+ const $in = targetVal.filter((x) => sourceVal.some((y) => (0, isEqual_js_1.default)(x, y)));
193
193
  if ($in.length === 0) {
194
194
  actionOnEmptyIntersect(target, source, prependKey);
195
195
  }
196
196
  else if ($in.length === 1) {
197
- (0, set_1.default)(target, prependKey.slice(0, -1), $in[0]);
197
+ (0, set_js_1.default)(target, prependKey.slice(0, -1), $in[0]);
198
198
  return;
199
199
  }
200
200
  else {
201
- (0, set_1.default)(target, prependKey, $in);
201
+ (0, set_js_1.default)(target, prependKey, $in);
202
202
  }
203
203
  }
204
204
  return;
205
205
  }
206
- (0, set_1.default)(target, prependKey, sourceVal);
206
+ (0, set_js_1.default)(target, prependKey, sourceVal);
207
207
  return;
208
208
  }
209
209
  if (typeOfTargetVal !== "object" || typeOfSourceVal !== "object") {
210
- (0, set_1.default)(target, prependKey, sourceVal);
210
+ (0, set_js_1.default)(target, prependKey, sourceVal);
211
211
  return;
212
212
  }
213
213
  // both are objects
@@ -233,12 +233,21 @@ function makeDefaultOptions(options) {
233
233
  }
234
234
  return options;
235
235
  }
236
+ function moveProperty(source, target, key) {
237
+ if (!Object.prototype.hasOwnProperty.call(source, key)) {
238
+ return;
239
+ }
240
+ target[key] = source[key];
241
+ delete source[key];
242
+ }
236
243
  function mergeQuery(target, source, options) {
237
244
  const fullOptions = makeDefaultOptions(options);
238
245
  const { filters: targetFilters, query: targetQuery } = (0, filterQuery_1.filterQuery)(target, {
239
246
  operators: fullOptions.operators,
240
247
  service: fullOptions.service
241
248
  });
249
+ moveProperty(targetFilters, targetQuery, "$or");
250
+ moveProperty(targetFilters, targetQuery, "$and");
242
251
  if (target.$limit) {
243
252
  targetFilters.$limit = target.$limit;
244
253
  }
@@ -248,6 +257,8 @@ function mergeQuery(target, source, options) {
248
257
  operators: fullOptions.operators,
249
258
  service: fullOptions.service
250
259
  });
260
+ moveProperty(sourceFilters, sourceQuery, "$or");
261
+ moveProperty(sourceFilters, sourceQuery, "$and");
251
262
  if (source.$limit) {
252
263
  sourceFilters.$limit = source.$limit;
253
264
  }
@@ -265,13 +276,13 @@ function mergeQuery(target, source, options) {
265
276
  handleArray(targetFilters, sourceFilters, ["$select"], fullOptions);
266
277
  // remaining filters
267
278
  delete sourceFilters["$select"];
268
- (0, merge_1.default)(targetFilters, sourceFilters);
279
+ (0, merge_js_1.default)(targetFilters, sourceFilters);
269
280
  //#endregion
270
281
  //#region '$or' / '$and'
271
282
  if ((options === null || options === void 0 ? void 0 : options.useLogicalConjunction) &&
272
283
  (options.defaultHandle === "combine" ||
273
284
  options.defaultHandle === "intersect") &&
274
- !(0, isEmpty_1.default)(targetQuery)) {
285
+ !(0, isEmpty_js_1.default)(targetQuery)) {
275
286
  const logicalOp = (options.defaultHandle === "combine")
276
287
  ? "$or"
277
288
  : "$and";
@@ -301,13 +312,13 @@ function getParentProp(target, path) {
301
312
  return target;
302
313
  }
303
314
  const pathOneUp = path.slice(0, -1);
304
- return (0, get_1.default)(target, pathOneUp);
315
+ return (0, get_js_1.default)(target, pathOneUp);
305
316
  }
306
317
  function cleanOr(target) {
307
318
  if (!target || !Array.isArray(target) || target.length <= 0) {
308
319
  return target;
309
320
  }
310
- if (target.some(x => (0, isEmpty_1.default)(x))) {
321
+ if (target.some(x => (0, isEmpty_js_1.default)(x))) {
311
322
  return undefined;
312
323
  }
313
324
  else {
@@ -318,11 +329,11 @@ function cleanAnd(target) {
318
329
  if (!target || !Array.isArray(target) || target.length <= 0) {
319
330
  return target;
320
331
  }
321
- if (target.every(x => (0, isEmpty_1.default)(x))) {
332
+ if (target.every(x => (0, isEmpty_js_1.default)(x))) {
322
333
  return undefined;
323
334
  }
324
335
  else {
325
- target = target.filter(x => !(0, isEmpty_1.default)(x));
336
+ target = target.filter(x => !(0, isEmpty_js_1.default)(x));
326
337
  return arrayWithoutDuplicates(target);
327
338
  }
328
339
  }
@@ -330,5 +341,5 @@ function arrayWithoutDuplicates(target) {
330
341
  if (!target || !Array.isArray(target)) {
331
342
  return target;
332
343
  }
333
- return (0, uniqWith_1.default)(target, isEqual_1.default);
344
+ return (0, uniqWith_js_1.default)(target, isEqual_js_1.default);
334
345
  }
@@ -1,2 +1,2 @@
1
1
  import type { Path, PushSetOptions } from "../types";
2
- export declare const pushSet: (obj: Record<string, unknown>, path: string | Path, val: unknown, options?: PushSetOptions | undefined) => unknown[];
2
+ export declare const pushSet: (obj: Record<string, unknown>, path: string | Path, val: unknown, options?: PushSetOptions) => unknown[];
@@ -4,19 +4,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.pushSet = void 0;
7
- const isEqual_1 = __importDefault(require("lodash/isEqual"));
8
- const get_1 = __importDefault(require("lodash/get"));
9
- const set_1 = __importDefault(require("lodash/set"));
7
+ const isEqual_js_1 = __importDefault(require("lodash/isEqual.js"));
8
+ const get_js_1 = __importDefault(require("lodash/get.js"));
9
+ const set_js_1 = __importDefault(require("lodash/set.js"));
10
10
  const pushSet = (obj, path, val, options) => {
11
11
  options = options || {};
12
- let arr = (0, get_1.default)(obj, path);
12
+ let arr = (0, get_js_1.default)(obj, path);
13
13
  if (!arr || !Array.isArray(arr)) {
14
14
  arr = [val];
15
- (0, set_1.default)(obj, path, arr);
15
+ (0, set_js_1.default)(obj, path, arr);
16
16
  return arr;
17
17
  }
18
18
  else {
19
- if (options.unique && arr.some(x => (0, isEqual_1.default)(x, val))) {
19
+ if (options.unique && arr.some(x => (0, isEqual_js_1.default)(x, val))) {
20
20
  return arr;
21
21
  }
22
22
  arr.push(val);
@@ -1,2 +1,2 @@
1
- import { HookContext } from "@feathersjs/feathers";
1
+ import type { HookContext } from "@feathersjs/feathers";
2
2
  export declare const setResultEmpty: (context: HookContext) => HookContext;
@@ -0,0 +1,2 @@
1
+ import type { Query } from "@feathersjs/feathers";
2
+ export declare const validateQueryProperty: (query: any, operators?: string[]) => Query;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateQueryProperty = void 0;
4
+ const commons_1 = require("@feathersjs/commons");
5
+ const errors_1 = require("@feathersjs/errors");
6
+ const isPlainObject = (value) => commons_1._.isObject(value) && value.constructor === {}.constructor;
7
+ const validateQueryProperty = (query, operators = []) => {
8
+ if (!isPlainObject(query)) {
9
+ return query;
10
+ }
11
+ for (const key of Object.keys(query)) {
12
+ if (key.startsWith("$") && !operators.includes(key)) {
13
+ throw new errors_1.BadRequest(`Invalid query parameter ${key}`, query);
14
+ }
15
+ const value = query[key];
16
+ if (isPlainObject(value)) {
17
+ query[key] = (0, exports.validateQueryProperty)(value, operators);
18
+ }
19
+ }
20
+ return Object.assign({}, query);
21
+ };
22
+ exports.validateQueryProperty = validateQueryProperty;
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "feathers-utils",
3
- "version": "2.0.0-0",
3
+ "version": "2.0.0-2",
4
4
  "description": "Some utils for projects using '@feathersjs/feathers'",
5
5
  "author": "fratzinger",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "https://github.com/fratzinger/feathers-utils"
9
9
  },
10
+ "engines": {
11
+ "node": ">= 14"
12
+ },
10
13
  "homepage": "https://github.com/fratzinger/feathers-utils",
11
14
  "license": "MIT",
12
15
  "main": "dist/",
@@ -21,6 +24,14 @@
21
24
  "directories": {
22
25
  "dist": "dist"
23
26
  },
27
+ "files": [
28
+ "CHANGELOG.md",
29
+ "LICENSE",
30
+ "README.md",
31
+ "src/**",
32
+ "lib/**",
33
+ "dist/**"
34
+ ],
24
35
  "scripts": {
25
36
  "build": "shx rm -rf dist/ && npm run tsc",
26
37
  "tsc": "tsc -p tsconfig.json && tsc -p tsconfig-esm.json",
@@ -31,29 +42,32 @@
31
42
  "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
32
43
  },
33
44
  "dependencies": {
34
- "@feathersjs/adapter-commons": "5.0.0-pre.15",
35
- "@feathersjs/errors": "5.0.0-pre.15",
36
- "@feathersjs/feathers": "5.0.0-pre.15",
37
- "feathers-hooks-common": "^5.0.6",
45
+ "@feathersjs/adapter-commons": "5.0.0-pre.29",
46
+ "@feathersjs/errors": "5.0.0-pre.29",
47
+ "@feathersjs/feathers": "5.0.0-pre.29",
48
+ "feathers-hooks-common": "^6.1.5",
38
49
  "lodash": "^4.17.21",
39
- "type-fest": "^2.5.4"
50
+ "type-fest": "^2.19.0"
40
51
  },
41
52
  "devDependencies": {
42
- "@istanbuljs/nyc-config-typescript": "^1.0.1",
43
- "@types/lodash": "^4.14.168",
44
- "@types/mocha": "^8.2.2",
45
- "@types/node": "^15.0.1",
46
- "@typescript-eslint/eslint-plugin": "^4.22.0",
47
- "@typescript-eslint/parser": "^4.22.0",
53
+ "@feathersjs/memory": "^5.0.0-pre.20",
54
+ "@istanbuljs/nyc-config-typescript": "^1.0.2",
55
+ "@types/lodash": "^4.14.185",
56
+ "@types/mocha": "^9.1.1",
57
+ "@types/node": "^18.7.18",
58
+ "@typescript-eslint/eslint-plugin": "^5.37.0",
59
+ "@typescript-eslint/parser": "^5.37.0",
48
60
  "cross-env": "^7.0.3",
49
- "eslint": "^7.25.0",
50
- "eslint-plugin-security": "^1.4.0",
61
+ "eslint": "^8.23.1",
62
+ "eslint-import-resolver-typescript": "^3.5.1",
63
+ "eslint-plugin-import": "^2.26.0",
64
+ "eslint-plugin-security": "^1.5.0",
51
65
  "feathers-memory": "^4.1.0",
52
- "mocha": "^8.3.2",
53
- "np": "^7.5.0",
66
+ "mocha": "^10.0.0",
67
+ "np": "^7.6.2",
54
68
  "nyc": "^15.1.0",
55
- "shx": "^0.3.3",
56
- "ts-node": "^9.1.1",
57
- "typescript": "^4.2.4"
69
+ "shx": "^0.3.4",
70
+ "ts-node": "^10.9.1",
71
+ "typescript": "^4.8.3"
58
72
  }
59
73
  }
@@ -0,0 +1,14 @@
1
+ import type { FilterQueryOptions } from "@feathersjs/adapter-commons";
2
+ import { validateQueryProperty } from "../utils/validateQueryProperty";
3
+
4
+ export const filterArray = () => (arr: any, { operators }: FilterQueryOptions) => {
5
+ if (arr && !Array.isArray(arr)) {
6
+ throw new Error("Invalid query parameter $and. It has to be an array");
7
+ }
8
+
9
+ if (Array.isArray(arr)) {
10
+ return arr.map((current) => validateQueryProperty(current, operators));
11
+ }
12
+
13
+ return arr;
14
+ };
@@ -3,8 +3,10 @@ import { shouldSkip } from "../utils/shouldSkip";
3
3
  import { isMulti } from "../utils/isMulti";
4
4
 
5
5
  import type { HookContext } from "@feathersjs/feathers";
6
+ import type { ReturnSyncHook } from "../types";
6
7
 
7
- export function checkMulti(): ((context: HookContext) => HookContext) {
8
+ export function checkMulti(
9
+ ): ReturnSyncHook {
8
10
  return (context: HookContext): HookContext => {
9
11
  if (shouldSkip("checkMulti", context)) { return context; }
10
12
  const { service, method } = context;
@@ -0,0 +1,45 @@
1
+ import type { HookContext } from "@feathersjs/feathers";
2
+ import { checkContext } from "feathers-hooks-common";
3
+ import type { CreateRelatedOptions } from "../types";
4
+ import { getItemsIsArray } from "../utils/getItemsIsArray";
5
+
6
+ export function createRelated<S = Record<string, any>>({
7
+ service,
8
+ multi = true,
9
+ data,
10
+ createItemsInDataArraySeparately = true
11
+ }: CreateRelatedOptions<S>) {
12
+ if (!service || !data) {
13
+ throw "initialize hook 'createRelated' completely!";
14
+ }
15
+ return async (context: HookContext): Promise<HookContext> => {
16
+ // @ts-expect-error wait for feathers-hooks-common to update
17
+ checkContext(context, "after", undefined, "createRelated");
18
+
19
+ const { items } = getItemsIsArray(context);
20
+
21
+ let dataToCreate = (await Promise.all(
22
+ items.map(async item => data(item, context))
23
+ )).filter(x => !!x);
24
+
25
+ if (createItemsInDataArraySeparately) {
26
+ dataToCreate = dataToCreate.flat();
27
+ }
28
+
29
+ if (!dataToCreate || dataToCreate.length <= 0) {
30
+ return context;
31
+ }
32
+
33
+ if (multi) {
34
+ await context.app.service(service as string).create(dataToCreate);
35
+ } else {
36
+ await Promise.all(
37
+ dataToCreate.map(async item =>
38
+ context.app.service(service as string).create(item)
39
+ )
40
+ );
41
+ }
42
+
43
+ return context;
44
+ };
45
+ }
@@ -0,0 +1,56 @@
1
+ import type { HookContext } from "@feathersjs/feathers";
2
+ import { checkContext } from "feathers-hooks-common";
3
+ import type { OnDeleteOptions } from "../types";
4
+ import { getItemsIsArray } from "../utils/getItemsIsArray";
5
+
6
+ export function onDelete<S = Record<string, any>>(
7
+ service: keyof S,
8
+ {
9
+ keyThere,
10
+ keyHere = "id",
11
+ onDelete = "cascade",
12
+ blocking = true
13
+ }: OnDeleteOptions) {
14
+ if (!service || !keyThere) {
15
+ throw "initialize hook 'removeRelated' completely!";
16
+ }
17
+ if (!["cascade", "set null"].includes(onDelete)) {
18
+ throw "onDelete must be 'cascade' or 'set null'";
19
+ }
20
+
21
+ return async (context: HookContext): Promise<HookContext> => {
22
+ // @ts-expect-error wait for feathers-hooks-common to update
23
+ checkContext(context, "after", "remove", "onDelete");
24
+
25
+ const { items } = getItemsIsArray(context);
26
+
27
+ let ids = items.map(x => x[keyHere]).filter(x => !!x);
28
+ ids = [...new Set(ids)];
29
+
30
+ if (!ids || ids.length <= 0) { return context; }
31
+
32
+ const params = {
33
+ query: {
34
+ [keyThere]: {
35
+ $in: ids
36
+ }
37
+ },
38
+ paginate: false
39
+ };
40
+
41
+ let promise;
42
+
43
+ if (onDelete === "cascade") {
44
+ promise = context.app.service(service as string).remove(null, params);
45
+ } else if (onDelete === "set null") {
46
+ const data = { [keyThere]: null };
47
+ promise = context.app.service(service as string).patch(null, data, params);
48
+ }
49
+
50
+ if (blocking) {
51
+ await promise;
52
+ }
53
+
54
+ return context;
55
+ };
56
+ }
@@ -0,0 +1,42 @@
1
+ import type { HookContext, Params, Query } from "@feathersjs/feathers";
2
+ import { checkContext } from "feathers-hooks-common";
3
+ import type { RemoveRelatedOptions } from "../types";
4
+ import { getItemsIsArray } from "../utils/getItemsIsArray";
5
+
6
+ export function removeRelated<S = Record<string, any>>({
7
+ service,
8
+ keyThere,
9
+ keyHere = "id",
10
+ blocking = true
11
+ }: RemoveRelatedOptions<S>) {
12
+ if (!service || !keyThere) {
13
+ throw "initialize hook 'removeRelated' completely!";
14
+ }
15
+ return async (context: HookContext): Promise<HookContext> => {
16
+ // @ts-expect-error wait for feathers-hooks-common to update
17
+ checkContext(context, "after", "remove", "removeRelated");
18
+
19
+ const { items } = getItemsIsArray(context);
20
+
21
+ let ids = items.map(x => x[keyHere]).filter(x => !!x);
22
+ ids = [...new Set(ids)];
23
+
24
+ if (!ids || ids.length <= 0) { return context; }
25
+
26
+ // feathers does not accept `paginate: false` for remove, but some adapters need it to work properly
27
+ const promise: Promise<any> = context.app.service(service as string).remove(null, {
28
+ query: {
29
+ [keyThere]: {
30
+ $in: ids
31
+ }
32
+ },
33
+ paginate: false
34
+ } as Params<Query>);
35
+
36
+ if (blocking) {
37
+ await promise;
38
+ }
39
+
40
+ return context;
41
+ };
42
+ }
@@ -1,13 +1,11 @@
1
- import { getItems } from "feathers-hooks-common";
2
-
3
1
  import { shouldSkip } from "../utils/shouldSkip";
4
2
 
5
- import type { HookRunPerItemOptions } from "../types";
3
+ import type { HookRunPerItemOptions, ReturnAsyncHook, Promisable } from "../types";
6
4
  import type { HookContext } from "@feathersjs/feathers";
7
- import type { Promisable } from "type-fest";
5
+ import { getItemsIsArray } from "../utils/getItemsIsArray";
8
6
 
9
7
  const makeOptions = (
10
- options: HookRunPerItemOptions
8
+ options?: HookRunPerItemOptions
11
9
  ): Required<HookRunPerItemOptions> => {
12
10
  options = options || {};
13
11
  return Object.assign({
@@ -16,16 +14,15 @@ const makeOptions = (
16
14
  };
17
15
 
18
16
  export const runPerItem = (
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
18
  actionPerItem: (item: any, context: HookContext) => Promisable<any>,
20
- options: HookRunPerItemOptions
21
- ): ((context: HookContext) => Promise<HookContext>) => {
22
- options = makeOptions(options);
19
+ _options?: HookRunPerItemOptions
20
+ ): ReturnAsyncHook => {
21
+ const options = makeOptions(_options);
23
22
  return async (context: HookContext): Promise<HookContext> => {
24
23
  if (shouldSkip("runForItems", context)) { return context; }
25
-
26
- //@ts-expect-error type error because feathers-hooks-common is feathers@4
27
- let items = getItems(context);
28
- items = (Array.isArray(items)) ? items : [items];
24
+
25
+ const { items } = getItemsIsArray(context);
29
26
 
30
27
  const promises = items.map(async (item: unknown) => {
31
28
  await actionPerItem(item, context);