feathers-utils 1.7.1 → 1.9.1

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 (51) hide show
  1. package/README.md +13 -8
  2. package/dist/esm/hooks/runPerItem.d.ts +2 -1
  3. package/dist/esm/hooks/runPerItem.js +3 -1
  4. package/dist/esm/hooks/setData.js +8 -1
  5. package/dist/esm/index.d.ts +3 -2
  6. package/dist/esm/index.js +2 -1
  7. package/dist/esm/types.d.ts +8 -2
  8. package/dist/esm/utils/getItemsIsArray.d.ts +3 -0
  9. package/dist/esm/utils/getItemsIsArray.js +10 -0
  10. package/dist/esm/utils/getPaginate.d.ts +1 -1
  11. package/dist/esm/utils/isPaginated.d.ts +1 -1
  12. package/dist/esm/utils/setResultEmpty.d.ts +1 -1
  13. package/dist/hooks/checkMulti.js +2 -2
  14. package/dist/hooks/runPerItem.d.ts +2 -1
  15. package/dist/hooks/runPerItem.js +5 -3
  16. package/dist/hooks/setData.js +13 -6
  17. package/dist/index.d.ts +3 -2
  18. package/dist/index.js +5 -3
  19. package/dist/mixins/debounce-mixin/DebouncedStore.js +2 -2
  20. package/dist/mixins/debounce-mixin/index.js +1 -1
  21. package/dist/types.d.ts +8 -2
  22. package/dist/utils/filterQuery.js +2 -2
  23. package/dist/utils/getItemsIsArray.d.ts +3 -0
  24. package/dist/utils/getItemsIsArray.js +14 -0
  25. package/dist/utils/getPaginate.d.ts +1 -1
  26. package/dist/utils/isPaginated.d.ts +1 -1
  27. package/dist/utils/isPaginated.js +1 -1
  28. package/dist/utils/markHookForSkip.js +1 -1
  29. package/dist/utils/mergeQuery/index.js +38 -38
  30. package/dist/utils/pushSet.js +3 -3
  31. package/dist/utils/setResultEmpty.d.ts +1 -1
  32. package/dist/utils/setResultEmpty.js +2 -2
  33. package/package.json +29 -18
  34. package/src/hooks/runPerItem.ts +3 -1
  35. package/src/hooks/setData.ts +8 -1
  36. package/src/index.ts +2 -1
  37. package/src/mixins/debounce-mixin/DebouncedStore.ts +48 -48
  38. package/src/types.ts +13 -2
  39. package/src/utils/getItemsIsArray.ts +15 -0
  40. package/src/utils/getPaginate.ts +1 -1
  41. package/src/utils/isPaginated.ts +1 -1
  42. package/src/utils/setResultEmpty.ts +1 -1
  43. package/.eslintignore +0 -3
  44. package/.eslintrc.js +0 -44
  45. package/.gitlab-ci.yml +0 -11
  46. package/.mocharc.js +0 -11
  47. package/.nycrc.json +0 -22
  48. package/index.js +0 -9
  49. package/tsconfig-esm.json +0 -9
  50. package/tsconfig.json +0 -16
  51. package/tsconfig.test.json +0 -13
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # feathers-utils
2
2
 
3
- ![npm](https://img.shields.io/npm/v/feathers-utils)
4
- ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/fratzinger/feathers-utils/Node.js%20CI)
5
- ![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/fratzinger/feathers-utils)
6
- ![Code Climate coverage](https://img.shields.io/codeclimate/coverage/fratzinger/feathers-utils)
7
- ![David](https://img.shields.io/david/fratzinger/feathers-casl)
8
- ![npm](https://img.shields.io/npm/dm/feathers-utils)
3
+ [![npm](https://img.shields.io/npm/v/feathers-utils)](https://www.npmjs.com/package/feathers-utils)
4
+ [![GitHub Workflow Status](https://img.shields.io/github/workflow/status/fratzinger/feathers-utils/Node.js%20CI)](https://github.com/fratzinger/feathers-utils/actions/workflows/node.js.yml?query=branch%3Amain)
5
+ [![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/fratzinger/feathers-utils)](https://codeclimate.com/github/fratzinger/feathers-utils)
6
+ [![Code Climate coverage](https://img.shields.io/codeclimate/coverage/fratzinger/feathers-utils)](https://codeclimate.com/github/fratzinger/feathers-utils)
7
+ [![libraries.io](https://img.shields.io/librariesio/release/npm/feathers-utils)](https://libraries.io/npm/feathers-utils)
8
+ [![npm](https://img.shields.io/npm/dm/feathers-utils)](https://www.npmjs.com/package/feathers-utils)
9
9
  [![GitHub license](https://img.shields.io/github/license/fratzinger/feathers-utils)](https://github.com/fratzinger/feathers-utils/blob/main/LICENSE.md)
10
10
 
11
11
 
@@ -19,8 +19,9 @@ npm i feathers-utils
19
19
 
20
20
  ### Hooks
21
21
 
22
- - `hooks/checkMulti()`: throws if the request is **multi** data, but the service has `allowsMulti(method)` returns `false`
23
- - `hooks/setData({ allowUndefined: Boolean })`
22
+ - `checkMulti()`: throws if the request is **multi** data, but the service has `allowsMulti(method)` returns `false`
23
+ - `runPerItem`
24
+ - `setData({ allowUndefined: Boolean })`
24
25
 
25
26
  ### Mixins
26
27
 
@@ -30,9 +31,13 @@ npm i feathers-utils
30
31
 
31
32
  - `addHook`: add hooks to specific services
32
33
  - `filterQuery`
34
+ - `getItemsIsArray(context)`: returns `{ items: any[], isArray: boolean }`
35
+ - `getPaginate`
33
36
  - `isMulti(context) => Boolean`: returns true, if `find`, `create/patch/remove`: multi
37
+ - `isPaginated`
34
38
  - `markHookForSkip`: add hookName to `context.params.skipHooks` - also see `shouldSkip`
35
39
  - `mergeQuery`: deeply merges queries
36
40
  - `mergeArrays`: merges arrays with intersection options
37
41
  - `pushSet`: if existing array: *push*, else *set*
42
+ - `setResultEmpty`
38
43
  - `shouldSkip`: checks `context.params.skipHooks` for `'all' | '${hookName}' | '${type}:${hookName}'` - also see `markHookForSkip`
@@ -1,3 +1,4 @@
1
1
  import type { HookRunPerItemOptions } from "../types";
2
2
  import type { HookContext } from "@feathersjs/feathers";
3
- export declare const runPerItem: (actionPerItem: (item: any, context: HookContext) => Promise<any>, options: HookRunPerItemOptions) => (context: HookContext) => Promise<HookContext>;
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>;
@@ -6,7 +6,9 @@ const makeOptions = (options) => {
6
6
  wait: true
7
7
  }, options);
8
8
  };
9
- export const runPerItem = (actionPerItem, options) => {
9
+ export const runPerItem = (
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ actionPerItem, options) => {
10
12
  options = makeOptions(options);
11
13
  return async (context) => {
12
14
  if (shouldSkip("runForItems", context)) {
@@ -23,7 +23,14 @@ export function setData(from, to, _options) {
23
23
  }
24
24
  const val = _get(context, from);
25
25
  items.forEach((item) => {
26
- if (!options.overwrite && _has(item, to)) {
26
+ let overwrite;
27
+ if (typeof options.overwrite === "function") {
28
+ overwrite = options.overwrite(item, context);
29
+ }
30
+ else {
31
+ overwrite = options.overwrite;
32
+ }
33
+ if (!overwrite && _has(item, to)) {
27
34
  return;
28
35
  }
29
36
  _set(item, to, val);
@@ -4,7 +4,7 @@ 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: any, context: import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<any>>) => Promise<any>, options: import("./types").HookRunPerItemOptions) => (context: import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<any>>) => Promise<import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<any>>>;
7
+ runPerItem: (actionPerItem: (item: any, context: import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<any>>) => any, options: import("./types").HookRunPerItemOptions) => (context: import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<any>>) => Promise<import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<any>>>;
8
8
  };
9
9
  export { checkMulti };
10
10
  export { setData };
@@ -25,6 +25,7 @@ export { mergeArrays } from "./utils/mergeQuery/mergeArrays";
25
25
  export { pushSet } from "./utils/pushSet";
26
26
  export { setResultEmpty } from "./utils/setResultEmpty";
27
27
  export { markHookForSkip } from "./utils/markHookForSkip";
28
- export { shouldSkip } from "./utils/shouldSkip";
29
28
  export { filterQuery } from "./utils/filterQuery";
29
+ export { getItemsIsArray } from "./utils/getItemsIsArray";
30
+ export { shouldSkip } from "./utils/shouldSkip";
30
31
  export * from "./types";
package/dist/esm/index.js CHANGED
@@ -26,6 +26,7 @@ export { mergeArrays } from "./utils/mergeQuery/mergeArrays";
26
26
  export { pushSet } from "./utils/pushSet";
27
27
  export { setResultEmpty } from "./utils/setResultEmpty";
28
28
  export { markHookForSkip } from "./utils/markHookForSkip";
29
- export { shouldSkip } from "./utils/shouldSkip";
30
29
  export { filterQuery } from "./utils/filterQuery";
30
+ export { getItemsIsArray } from "./utils/getItemsIsArray";
31
+ export { shouldSkip } from "./utils/shouldSkip";
31
32
  export * from "./types";
@@ -1,12 +1,14 @@
1
- import type { Application, Service } from "@feathersjs/feathers";
1
+ import type { Application, HookContext, Service } from "@feathersjs/feathers";
2
2
  export declare type Path = Array<string | number>;
3
3
  export declare type HookType = "before" | "after" | "error";
4
4
  export declare type ServiceMethodName = "find" | "get" | "create" | "update" | "patch" | "remove";
5
5
  export declare type Handle = "target" | "source" | "combine" | "intersect" | "intersectOrFull";
6
6
  export declare type FirstLast = "first" | "last";
7
+ export declare type Predicate<T = any> = (item: T) => boolean;
8
+ export declare type PredicateWithContext<T = any> = (item: T, context: HookContext) => boolean;
7
9
  export interface HookSetDataOptions {
8
10
  allowUndefined?: boolean;
9
- overwrite?: boolean;
11
+ overwrite?: boolean | PredicateWithContext;
10
12
  }
11
13
  export interface AddHookOptions {
12
14
  types: HookType[];
@@ -57,3 +59,7 @@ export interface FilterQueryResult {
57
59
  paginate?: unknown;
58
60
  [key: string]: unknown;
59
61
  }
62
+ export interface GetItemsIsArrayOptions<T = any> {
63
+ items: T[];
64
+ isArray: boolean;
65
+ }
@@ -0,0 +1,3 @@
1
+ import type { HookContext } from "@feathersjs/feathers";
2
+ import type { GetItemsIsArrayOptions } from "..";
3
+ export declare const getItemsIsArray: <T = any>(context: HookContext) => GetItemsIsArrayOptions<T>;
@@ -0,0 +1,10 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
+ export const getItemsIsArray = (context) => {
3
+ let itemOrItems = context.type === "before" ? context.data : context.result;
4
+ itemOrItems = itemOrItems && context.method === "find" ? (itemOrItems.data || itemOrItems) : itemOrItems;
5
+ const isArray = Array.isArray(itemOrItems);
6
+ return {
7
+ items: (isArray) ? itemOrItems : [itemOrItems],
8
+ isArray
9
+ };
10
+ };
@@ -1,2 +1,2 @@
1
- import { HookContext, PaginationOptions } from "@feathersjs/feathers";
1
+ import type { HookContext, PaginationOptions } from "@feathersjs/feathers";
2
2
  export declare const getPaginate: (context: HookContext) => PaginationOptions | undefined;
@@ -1,2 +1,2 @@
1
- import { HookContext } from "@feathersjs/feathers";
1
+ import type { HookContext } from "@feathersjs/feathers";
2
2
  export declare const isPaginated: (context: HookContext) => boolean;
@@ -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;
@@ -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
- export declare const runPerItem: (actionPerItem: (item: any, context: HookContext) => Promise<any>, options: HookRunPerItemOptions) => (context: HookContext) => Promise<HookContext>;
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>;
@@ -18,13 +18,15 @@ const makeOptions = (options) => {
18
18
  wait: true
19
19
  }, options);
20
20
  };
21
- const runPerItem = (actionPerItem, options) => {
21
+ const runPerItem = (
22
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
+ actionPerItem, options) => {
22
24
  options = makeOptions(options);
23
25
  return (context) => __awaiter(void 0, void 0, void 0, function* () {
24
- if (shouldSkip_1.shouldSkip("runForItems", context)) {
26
+ if ((0, shouldSkip_1.shouldSkip)("runForItems", context)) {
25
27
  return context;
26
28
  }
27
- let items = feathers_hooks_common_1.getItems(context);
29
+ let items = (0, feathers_hooks_common_1.getItems)(context);
28
30
  items = (Array.isArray(items)) ? items : [items];
29
31
  const promises = items.map((item) => __awaiter(void 0, void 0, void 0, function* () {
30
32
  yield actionPerItem(item, context);
@@ -17,23 +17,30 @@ function setData(from, to, _options) {
17
17
  const options = Object.assign({}, defaultOptions, _options);
18
18
  return (context) => {
19
19
  var _a;
20
- let items = feathers_hooks_common_1.getItems(context);
20
+ let items = (0, feathers_hooks_common_1.getItems)(context);
21
21
  items = (Array.isArray(items)) ? items : [items];
22
- if (!has_1.default(context, from)) {
22
+ if (!(0, has_1.default)(context, from)) {
23
23
  if (!((_a = context.params) === null || _a === void 0 ? void 0 : _a.provider) || options.allowUndefined === true) {
24
24
  return context;
25
25
  }
26
- if (!options.overwrite && items.every((item) => has_1.default(item, to))) {
26
+ if (!options.overwrite && items.every((item) => (0, has_1.default)(item, to))) {
27
27
  return context;
28
28
  }
29
29
  throw new errors_1.Forbidden(`Expected field ${from.toString()} not available`);
30
30
  }
31
- const val = get_1.default(context, from);
31
+ const val = (0, get_1.default)(context, from);
32
32
  items.forEach((item) => {
33
- if (!options.overwrite && has_1.default(item, to)) {
33
+ let overwrite;
34
+ if (typeof options.overwrite === "function") {
35
+ overwrite = options.overwrite(item, context);
36
+ }
37
+ else {
38
+ overwrite = options.overwrite;
39
+ }
40
+ if (!overwrite && (0, has_1.default)(item, to)) {
34
41
  return;
35
42
  }
36
- set_1.default(item, to, val);
43
+ (0, set_1.default)(item, to, val);
37
44
  });
38
45
  return context;
39
46
  };
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@ 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: any, context: import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<any>>) => Promise<any>, options: import("./types").HookRunPerItemOptions) => (context: import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<any>>) => Promise<import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<any>>>;
7
+ runPerItem: (actionPerItem: (item: any, context: import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<any>>) => any, options: import("./types").HookRunPerItemOptions) => (context: import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<any>>) => Promise<import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<any>>>;
8
8
  };
9
9
  export { checkMulti };
10
10
  export { setData };
@@ -25,6 +25,7 @@ export { mergeArrays } from "./utils/mergeQuery/mergeArrays";
25
25
  export { pushSet } from "./utils/pushSet";
26
26
  export { setResultEmpty } from "./utils/setResultEmpty";
27
27
  export { markHookForSkip } from "./utils/markHookForSkip";
28
- export { shouldSkip } from "./utils/shouldSkip";
29
28
  export { filterQuery } from "./utils/filterQuery";
29
+ export { getItemsIsArray } from "./utils/getItemsIsArray";
30
+ export { shouldSkip } from "./utils/shouldSkip";
30
31
  export * from "./types";
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.setResultEmpty = exports.pushSet = exports.mergeArrays = exports.mergeQuery = exports.isPaginated = exports.isMulti = exports.getPaginate = exports.addHook = exports.DebouncedStore = exports.debounceMixin = exports.mixins = exports.runPerItem = exports.setData = exports.checkMulti = exports.hooks = void 0;
13
+ exports.shouldSkip = exports.getItemsIsArray = exports.filterQuery = exports.markHookForSkip = exports.setResultEmpty = exports.pushSet = exports.mergeArrays = exports.mergeQuery = exports.isPaginated = exports.isMulti = exports.getPaginate = exports.addHook = 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; } });
@@ -48,8 +48,10 @@ var setResultEmpty_1 = require("./utils/setResultEmpty");
48
48
  Object.defineProperty(exports, "setResultEmpty", { enumerable: true, get: function () { return setResultEmpty_1.setResultEmpty; } });
49
49
  var markHookForSkip_1 = require("./utils/markHookForSkip");
50
50
  Object.defineProperty(exports, "markHookForSkip", { enumerable: true, get: function () { return markHookForSkip_1.markHookForSkip; } });
51
- var shouldSkip_1 = require("./utils/shouldSkip");
52
- Object.defineProperty(exports, "shouldSkip", { enumerable: true, get: function () { return shouldSkip_1.shouldSkip; } });
53
51
  var filterQuery_1 = require("./utils/filterQuery");
54
52
  Object.defineProperty(exports, "filterQuery", { enumerable: true, get: function () { return filterQuery_1.filterQuery; } });
53
+ var getItemsIsArray_1 = require("./utils/getItemsIsArray");
54
+ Object.defineProperty(exports, "getItemsIsArray", { enumerable: true, get: function () { return getItemsIsArray_1.getItemsIsArray; } });
55
+ var shouldSkip_1 = require("./utils/shouldSkip");
56
+ Object.defineProperty(exports, "shouldSkip", { enumerable: true, get: function () { return shouldSkip_1.shouldSkip; } });
55
57
  __exportStar(require("./types"), exports);
@@ -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);
@@ -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))
package/dist/types.d.ts CHANGED
@@ -1,12 +1,14 @@
1
- import type { Application, Service } from "@feathersjs/feathers";
1
+ import type { Application, HookContext, Service } from "@feathersjs/feathers";
2
2
  export declare type Path = Array<string | number>;
3
3
  export declare type HookType = "before" | "after" | "error";
4
4
  export declare type ServiceMethodName = "find" | "get" | "create" | "update" | "patch" | "remove";
5
5
  export declare type Handle = "target" | "source" | "combine" | "intersect" | "intersectOrFull";
6
6
  export declare type FirstLast = "first" | "last";
7
+ export declare type Predicate<T = any> = (item: T) => boolean;
8
+ export declare type PredicateWithContext<T = any> = (item: T, context: HookContext) => boolean;
7
9
  export interface HookSetDataOptions {
8
10
  allowUndefined?: boolean;
9
- overwrite?: boolean;
11
+ overwrite?: boolean | PredicateWithContext;
10
12
  }
11
13
  export interface AddHookOptions {
12
14
  types: HookType[];
@@ -57,3 +59,7 @@ export interface FilterQueryResult {
57
59
  paginate?: unknown;
58
60
  [key: string]: unknown;
59
61
  }
62
+ export interface GetItemsIsArrayOptions<T = any> {
63
+ items: T[];
64
+ isArray: boolean;
65
+ }
@@ -25,9 +25,9 @@ function filterQuery(query, options) {
25
25
  return service.filterQuery({ query }, optionsForFilterQuery);
26
26
  }
27
27
  else {
28
- return adapter_commons_1.filterQuery(query, optionsForFilterQuery);
28
+ return (0, adapter_commons_1.filterQuery)(query, optionsForFilterQuery);
29
29
  }
30
30
  }
31
- return adapter_commons_1.filterQuery(query, options);
31
+ return (0, adapter_commons_1.filterQuery)(query, options);
32
32
  }
33
33
  exports.filterQuery = filterQuery;
@@ -0,0 +1,3 @@
1
+ import type { HookContext } from "@feathersjs/feathers";
2
+ import type { GetItemsIsArrayOptions } from "..";
3
+ export declare const getItemsIsArray: <T = any>(context: HookContext) => GetItemsIsArrayOptions<T>;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getItemsIsArray = void 0;
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ const getItemsIsArray = (context) => {
6
+ let itemOrItems = context.type === "before" ? context.data : context.result;
7
+ itemOrItems = itemOrItems && context.method === "find" ? (itemOrItems.data || itemOrItems) : itemOrItems;
8
+ const isArray = Array.isArray(itemOrItems);
9
+ return {
10
+ items: (isArray) ? itemOrItems : [itemOrItems],
11
+ isArray
12
+ };
13
+ };
14
+ exports.getItemsIsArray = getItemsIsArray;
@@ -1,2 +1,2 @@
1
- import { HookContext, PaginationOptions } from "@feathersjs/feathers";
1
+ import type { HookContext, PaginationOptions } from "@feathersjs/feathers";
2
2
  export declare const getPaginate: (context: HookContext) => PaginationOptions | undefined;
@@ -1,2 +1,2 @@
1
- import { HookContext } from "@feathersjs/feathers";
1
+ import type { HookContext } from "@feathersjs/feathers";
2
2
  export declare const isPaginated: (context: HookContext) => boolean;
@@ -6,7 +6,7 @@ const isPaginated = (context) => {
6
6
  if (context.params.paginate === false) {
7
7
  return false;
8
8
  }
9
- const paginate = getPaginate_1.getPaginate(context);
9
+ const paginate = (0, getPaginate_1.getPaginate)(context);
10
10
  return !!paginate;
11
11
  };
12
12
  exports.isPaginated = isPaginated;
@@ -10,7 +10,7 @@ function markHookForSkip(hookName, type, context) {
10
10
  const combinedName = (t === "all")
11
11
  ? hookName
12
12
  : `${type}:${hookName}`;
13
- pushSet_1.pushSet(params, ["skipHooks"], combinedName, { unique: true });
13
+ (0, pushSet_1.pushSet)(params, ["skipHooks"], combinedName, { unique: true });
14
14
  });
15
15
  context.params = params;
16
16
  return context;
@@ -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 = get_1.default(target, key);
22
- const sourceVal = get_1.default(source, key);
21
+ const targetVal = (0, get_1.default)(target, key);
22
+ const sourceVal = (0, get_1.default)(source, key);
23
23
  if (!sourceVal && !targetVal) {
24
24
  return;
25
25
  }
26
- const handle = get_1.default(options, ["handle", ...key], options.defaultHandle);
27
- const arr = mergeArrays_1.mergeArrays(targetVal, sourceVal, handle, key, options.actionOnEmptyIntersect);
28
- set_1.default(target, key, arr);
26
+ const handle = (0, get_1.default)(options, ["handle", ...key], options.defaultHandle);
27
+ const arr = (0, mergeArrays_1.mergeArrays)(targetVal, sourceVal, handle, key, options.actionOnEmptyIntersect);
28
+ (0, set_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 (!has_1.default(source, prependKey)) {
55
+ if (!(0, has_1.default)(source, prependKey)) {
56
56
  return;
57
57
  }
58
- if (!has_1.default(target, prependKey)) {
59
- set_1.default(target, prependKey, get_1.default(source, prependKey));
58
+ if (!(0, has_1.default)(target, prependKey)) {
59
+ (0, set_1.default)(target, prependKey, (0, get_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) ? get_1.default(target, prependKey) : target;
67
+ return (prependKey.length > 0) ? (0, get_1.default)(target, prependKey) : target;
68
68
  };
69
69
  const getSourceVal = () => {
70
- return (prependKey.length > 0) ? get_1.default(source, prependKey) : source;
70
+ return (prependKey.length > 0) ? (0, get_1.default)(source, prependKey) : source;
71
71
  };
72
72
  const targetVal = getTargetVal();
73
73
  const sourceVal = getSourceVal();
74
- if (isEqual_1.default(targetVal, sourceVal)) {
74
+ if ((0, isEqual_1.default)(targetVal, sourceVal)) {
75
75
  return;
76
76
  }
77
77
  if (defaultHandle === "source") {
78
- set_1.default(target, prependKey, sourceVal);
78
+ (0, set_1.default)(target, prependKey, sourceVal);
79
79
  return;
80
80
  }
81
81
  if (targetVal === null || sourceVal === null) {
82
- set_1.default(target, prependKey, sourceVal);
82
+ (0, set_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
- set_1.default(target, prependKey, sourceVal);
90
+ (0, set_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
- set_1.default(target, prependKey, { $in: [...new Set([targetVal, sourceVal])] });
99
+ (0, set_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 && isEqual_1.default($in[0], otherVal)) {
114
- set_1.default(target, prependKey, otherVal);
113
+ if ($in.length === 1 && (0, isEqual_1.default)($in[0], otherVal)) {
114
+ (0, set_1.default)(target, prependKey, otherVal);
115
115
  return;
116
116
  }
117
117
  else if (defaultHandle === "combine") {
118
- if (!$in.some((x) => isEqual_1.default(x, otherVal))) {
118
+ if (!$in.some((x) => (0, isEqual_1.default)(x, otherVal))) {
119
119
  $in.push(otherVal);
120
120
  }
121
- set_1.default(target, `${prependKey}.$in`, $in);
121
+ (0, set_1.default)(target, `${prependKey}.$in`, $in);
122
122
  return;
123
123
  }
124
124
  else if (defaultHandle === "intersect") {
125
- if ($in.some((x) => isEqual_1.default(x, otherVal))) {
126
- set_1.default(target, prependKey, otherVal);
125
+ if ($in.some((x) => (0, isEqual_1.default)(x, otherVal))) {
126
+ (0, set_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) => isEqual_1.default(x, y)));
142
+ const newVals = sourceVal.filter((x) => !targetVal.some((y) => (0, isEqual_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) => isEqual_1.default(x, y)));
179
+ const newVals = sourceVal.filter((x) => !targetVal.some((y) => (0, isEqual_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
- set_1.default(target, prependKey, $in);
188
+ (0, set_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) => isEqual_1.default(x, y)));
192
+ const $in = targetVal.filter((x) => sourceVal.some((y) => (0, isEqual_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
- set_1.default(target, prependKey.slice(0, -1), $in[0]);
197
+ (0, set_1.default)(target, prependKey.slice(0, -1), $in[0]);
198
198
  return;
199
199
  }
200
200
  else {
201
- set_1.default(target, prependKey, $in);
201
+ (0, set_1.default)(target, prependKey, $in);
202
202
  }
203
203
  }
204
204
  return;
205
205
  }
206
- set_1.default(target, prependKey, sourceVal);
206
+ (0, set_1.default)(target, prependKey, sourceVal);
207
207
  return;
208
208
  }
209
209
  if (typeOfTargetVal !== "object" || typeOfSourceVal !== "object") {
210
- set_1.default(target, prependKey, sourceVal);
210
+ (0, set_1.default)(target, prependKey, sourceVal);
211
211
  return;
212
212
  }
213
213
  // both are objects
@@ -235,7 +235,7 @@ function makeDefaultOptions(options) {
235
235
  }
236
236
  function mergeQuery(target, source, options) {
237
237
  const fullOptions = makeDefaultOptions(options);
238
- const { filters: targetFilters, query: targetQuery } = filterQuery_1.filterQuery(target, {
238
+ const { filters: targetFilters, query: targetQuery } = (0, filterQuery_1.filterQuery)(target, {
239
239
  operators: fullOptions.operators,
240
240
  service: fullOptions.service
241
241
  });
@@ -244,7 +244,7 @@ function mergeQuery(target, source, options) {
244
244
  }
245
245
  let {
246
246
  // eslint-disable-next-line prefer-const
247
- filters: sourceFilters, query: sourceQuery } = filterQuery_1.filterQuery(source, {
247
+ filters: sourceFilters, query: sourceQuery } = (0, filterQuery_1.filterQuery)(source, {
248
248
  operators: fullOptions.operators,
249
249
  service: fullOptions.service
250
250
  });
@@ -265,13 +265,13 @@ function mergeQuery(target, source, options) {
265
265
  handleArray(targetFilters, sourceFilters, ["$select"], fullOptions);
266
266
  // remaining filters
267
267
  delete sourceFilters["$select"];
268
- merge_1.default(targetFilters, sourceFilters);
268
+ (0, merge_1.default)(targetFilters, sourceFilters);
269
269
  //#endregion
270
270
  //#region '$or' / '$and'
271
271
  if ((options === null || options === void 0 ? void 0 : options.useLogicalConjunction) &&
272
272
  (options.defaultHandle === "combine" ||
273
273
  options.defaultHandle === "intersect") &&
274
- !isEmpty_1.default(targetQuery)) {
274
+ !(0, isEmpty_1.default)(targetQuery)) {
275
275
  const logicalOp = (options.defaultHandle === "combine")
276
276
  ? "$or"
277
277
  : "$and";
@@ -301,13 +301,13 @@ function getParentProp(target, path) {
301
301
  return target;
302
302
  }
303
303
  const pathOneUp = path.slice(0, -1);
304
- return get_1.default(target, pathOneUp);
304
+ return (0, get_1.default)(target, pathOneUp);
305
305
  }
306
306
  function cleanOr(target) {
307
307
  if (!target || !Array.isArray(target) || target.length <= 0) {
308
308
  return target;
309
309
  }
310
- if (target.some(x => isEmpty_1.default(x))) {
310
+ if (target.some(x => (0, isEmpty_1.default)(x))) {
311
311
  return undefined;
312
312
  }
313
313
  else {
@@ -318,11 +318,11 @@ function cleanAnd(target) {
318
318
  if (!target || !Array.isArray(target) || target.length <= 0) {
319
319
  return target;
320
320
  }
321
- if (target.every(x => isEmpty_1.default(x))) {
321
+ if (target.every(x => (0, isEmpty_1.default)(x))) {
322
322
  return undefined;
323
323
  }
324
324
  else {
325
- target = target.filter(x => !isEmpty_1.default(x));
325
+ target = target.filter(x => !(0, isEmpty_1.default)(x));
326
326
  return arrayWithoutDuplicates(target);
327
327
  }
328
328
  }
@@ -330,5 +330,5 @@ function arrayWithoutDuplicates(target) {
330
330
  if (!target || !Array.isArray(target)) {
331
331
  return target;
332
332
  }
333
- return uniqWith_1.default(target, isEqual_1.default);
333
+ return (0, uniqWith_1.default)(target, isEqual_1.default);
334
334
  }
@@ -9,14 +9,14 @@ const get_1 = __importDefault(require("lodash/get"));
9
9
  const set_1 = __importDefault(require("lodash/set"));
10
10
  const pushSet = (obj, path, val, options) => {
11
11
  options = options || {};
12
- let arr = get_1.default(obj, path);
12
+ let arr = (0, get_1.default)(obj, path);
13
13
  if (!arr || !Array.isArray(arr)) {
14
14
  arr = [val];
15
- set_1.default(obj, path, arr);
15
+ (0, set_1.default)(obj, path, arr);
16
16
  return arr;
17
17
  }
18
18
  else {
19
- if (options.unique && arr.some(x => isEqual_1.default(x, val))) {
19
+ if (options.unique && arr.some(x => (0, isEqual_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;
@@ -7,9 +7,9 @@ const setResultEmpty = (context) => {
7
7
  if (context.result) {
8
8
  return context;
9
9
  }
10
- const multi = __1.isMulti(context);
10
+ const multi = (0, __1.isMulti)(context);
11
11
  if (multi) {
12
- if (context.method === "find" && isPaginated_1.isPaginated(context)) {
12
+ if (context.method === "find" && (0, isPaginated_1.isPaginated)(context)) {
13
13
  context.result = {
14
14
  total: 0,
15
15
  skip: 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feathers-utils",
3
- "version": "1.7.1",
3
+ "version": "1.9.1",
4
4
  "description": "Some utils for projects using '@feathersjs/feathers'",
5
5
  "author": "fratzinger",
6
6
  "repository": {
@@ -21,6 +21,14 @@
21
21
  "directories": {
22
22
  "dist": "dist"
23
23
  },
24
+ "files": [
25
+ "CHANGELOG.md",
26
+ "LICENSE",
27
+ "README.md",
28
+ "src/**",
29
+ "lib/**",
30
+ "dist/**"
31
+ ],
24
32
  "scripts": {
25
33
  "build": "shx rm -rf dist/ && npm run tsc",
26
34
  "tsc": "tsc -p tsconfig.json && tsc -p tsconfig-esm.json",
@@ -31,28 +39,31 @@
31
39
  "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
32
40
  },
33
41
  "dependencies": {
34
- "@feathersjs/adapter-commons": "^4.5.11",
35
- "@feathersjs/errors": "^4.5.11",
36
- "@feathersjs/feathers": "^4.5.11",
37
- "feathers-hooks-common": "^5.0.5",
38
- "lodash": "^4.17.21"
42
+ "@feathersjs/adapter-commons": "^4.5.12",
43
+ "@feathersjs/errors": "^4.5.12",
44
+ "@feathersjs/feathers": "^4.5.12",
45
+ "feathers-hooks-common": "^5.0.6",
46
+ "lodash": "^4.17.21",
47
+ "type-fest": "^2.11.1"
39
48
  },
40
49
  "devDependencies": {
41
- "@istanbuljs/nyc-config-typescript": "^1.0.1",
42
- "@types/lodash": "^4.14.168",
43
- "@types/mocha": "^8.2.2",
44
- "@types/node": "^15.0.1",
45
- "@typescript-eslint/eslint-plugin": "^4.22.0",
46
- "@typescript-eslint/parser": "^4.22.0",
50
+ "@istanbuljs/nyc-config-typescript": "^1.0.2",
51
+ "@types/lodash": "^4.14.178",
52
+ "@types/mocha": "^9.1.0",
53
+ "@types/node": "^17.0.14",
54
+ "@typescript-eslint/eslint-plugin": "^5.10.2",
55
+ "@typescript-eslint/parser": "^5.10.2",
47
56
  "cross-env": "^7.0.3",
48
- "eslint": "^7.25.0",
57
+ "eslint": "^8.8.0",
58
+ "eslint-import-resolver-typescript": "^2.5.0",
59
+ "eslint-plugin-import": "^2.25.4",
49
60
  "eslint-plugin-security": "^1.4.0",
50
61
  "feathers-memory": "^4.1.0",
51
- "mocha": "^8.3.2",
52
- "np": "^7.5.0",
62
+ "mocha": "^9.2.0",
63
+ "np": "^7.6.0",
53
64
  "nyc": "^15.1.0",
54
- "shx": "^0.3.3",
55
- "ts-node": "^9.1.1",
56
- "typescript": "^4.2.4"
65
+ "shx": "^0.3.4",
66
+ "ts-node": "^10.4.0",
67
+ "typescript": "^4.5.5"
57
68
  }
58
69
  }
@@ -4,6 +4,7 @@ import { shouldSkip } from "../utils/shouldSkip";
4
4
 
5
5
  import type { HookRunPerItemOptions } from "../types";
6
6
  import type { HookContext } from "@feathersjs/feathers";
7
+ import type { Promisable } from "type-fest";
7
8
 
8
9
  const makeOptions = (
9
10
  options: HookRunPerItemOptions
@@ -15,7 +16,8 @@ const makeOptions = (
15
16
  };
16
17
 
17
18
  export const runPerItem = (
18
- actionPerItem: (item: any, context: HookContext) => Promise<any>,
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ actionPerItem: (item: any, context: HookContext) => Promisable<any>,
19
21
  options: HookRunPerItemOptions
20
22
  ): ((context: HookContext) => Promise<HookContext>) => {
21
23
  options = makeOptions(options);
@@ -44,7 +44,14 @@ export function setData(
44
44
  const val = _get(context, from);
45
45
 
46
46
  items.forEach((item: Record<string, unknown>) => {
47
- if (!options.overwrite && _has(item, to)) { return; }
47
+ let overwrite: boolean;
48
+ if (typeof options.overwrite === "function") {
49
+ overwrite = options.overwrite(item, context);
50
+ } else {
51
+ overwrite = options.overwrite;
52
+ }
53
+
54
+ if (!overwrite && _has(item, to)) { return; }
48
55
 
49
56
  _set(item, to, val);
50
57
  });
package/src/index.ts CHANGED
@@ -33,8 +33,9 @@ export { pushSet } from "./utils/pushSet";
33
33
  export { setResultEmpty } from "./utils/setResultEmpty";
34
34
 
35
35
  export { markHookForSkip } from "./utils/markHookForSkip";
36
+ export { filterQuery } from "./utils/filterQuery";
37
+ export { getItemsIsArray } from "./utils/getItemsIsArray";
36
38
  export { shouldSkip } from "./utils/shouldSkip";
37
39
 
38
- export { filterQuery } from "./utils/filterQuery";
39
40
 
40
41
  export * from "./types";
@@ -18,58 +18,58 @@ export const makeDefaultOptions = (): DebouncedStoreOptions => {
18
18
  };
19
19
 
20
20
  export class DebouncedStore {
21
- private _app: Application;
22
- private _options: DebouncedStoreOptions;
23
- private _isRunningById: Record<string, unknown>;
24
- _queueById: Record<string, DebouncedFunc<((id: Id, action: DebouncedFunctionApp) => void | Promise<void>)>>;
25
- //_waitingById: Record<string, WaitingObject>;
26
- add;
27
- constructor(app: Application, options?: Partial<DebouncedStoreOptions>) {
28
- this._app = app;
29
- this._options = Object.assign(makeDefaultOptions(), options);
30
- this._queueById = {};
31
- this._isRunningById = {};
32
- //this._waitingById = {};
21
+ private _app: Application;
22
+ private _options: DebouncedStoreOptions;
23
+ private _isRunningById: Record<string, unknown>;
24
+ _queueById: Record<string, DebouncedFunc<((id: Id, action: DebouncedFunctionApp) => void | Promise<void>)>>;
25
+ //_waitingById: Record<string, WaitingObject>;
26
+ add;
27
+ constructor(app: Application, options?: Partial<DebouncedStoreOptions>) {
28
+ this._app = app;
29
+ this._options = Object.assign(makeDefaultOptions(), options);
30
+ this._queueById = {};
31
+ this._isRunningById = {};
32
+ //this._waitingById = {};
33
33
 
34
- this.add = this.debounceById(
35
- this.unbounced,
36
- this._options.wait,
37
- {
38
- leading: this._options.leading,
39
- maxWait: this._options.maxWait,
40
- trailing: this._options.trailing
41
- }
42
- );
43
- }
44
-
45
- private async unbounced(id: Id, action: DebouncedFunctionApp): Promise<void> {
46
- if (this._queueById[id] === undefined) {
47
- return;
34
+ this.add = this.debounceById(
35
+ this.unbounced,
36
+ this._options.wait,
37
+ {
38
+ leading: this._options.leading,
39
+ maxWait: this._options.maxWait,
40
+ trailing: this._options.trailing
48
41
  }
49
- delete this._queueById[id];
50
- this._isRunningById[id] = true;
51
- await action(this._app);
52
- delete this._isRunningById[id];
42
+ );
43
+ }
44
+
45
+ private async unbounced(id: Id, action: DebouncedFunctionApp): Promise<void> {
46
+ if (this._queueById[id] === undefined) {
47
+ return;
53
48
  }
49
+ delete this._queueById[id];
50
+ this._isRunningById[id] = true;
51
+ await action(this._app);
52
+ delete this._isRunningById[id];
53
+ }
54
54
 
55
- private debounceById(
56
- func: ((id: Id, action: DebouncedFunctionApp) => Promise<void>),
57
- wait: number,
58
- options?: Partial<DebouncedStoreOptions>
59
- ) {
60
- return (id: Id, action: ((app?: Application) => void | Promise<void>)) => {
61
- if (typeof this._queueById[id] === "function") {
62
- return this._queueById[id](id, action);
63
- }
64
-
65
- this._queueById[id] = _debounce((id, action) => {
66
- this.unbounced(id, action);
67
- }, wait, { ...options, leading: false }); // leading required for return promise
55
+ private debounceById(
56
+ func: ((id: Id, action: DebouncedFunctionApp) => Promise<void>),
57
+ wait: number,
58
+ options?: Partial<DebouncedStoreOptions>
59
+ ) {
60
+ return (id: Id, action: ((app?: Application) => void | Promise<void>)) => {
61
+ if (typeof this._queueById[id] === "function") {
68
62
  return this._queueById[id](id, action);
69
- };
70
- }
63
+ }
64
+
65
+ this._queueById[id] = _debounce((id, action) => {
66
+ this.unbounced(id, action);
67
+ }, wait, { ...options, leading: false }); // leading required for return promise
68
+ return this._queueById[id](id, action);
69
+ };
70
+ }
71
71
 
72
- cancel(id: Id): void {
73
- delete this._queueById[id];
74
- }
72
+ cancel(id: Id): void {
73
+ delete this._queueById[id];
74
+ }
75
75
  }
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Application, Service } from "@feathersjs/feathers";
1
+ import type { Application, HookContext, Service } from "@feathersjs/feathers";
2
2
 
3
3
  export type Path = Array<string|number>;
4
4
  export type HookType = "before" | "after" | "error";
@@ -7,11 +7,16 @@ export type ServiceMethodName = "find" | "get" | "create" | "update" | "patch" |
7
7
  export type Handle = "target" | "source" | "combine" | "intersect"| "intersectOrFull";
8
8
  export type FirstLast = "first" | "last";
9
9
 
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ export type Predicate<T = any> = (item: T) => boolean
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+ export type PredicateWithContext<T = any> = (item: T, context: HookContext) => boolean
14
+
10
15
  //#region hooks
11
16
 
12
17
  export interface HookSetDataOptions {
13
18
  allowUndefined?: boolean
14
- overwrite?: boolean
19
+ overwrite?: boolean | PredicateWithContext
15
20
  }
16
21
 
17
22
  export interface AddHookOptions {
@@ -86,4 +91,10 @@ export interface FilterQueryResult {
86
91
  [key: string]: unknown
87
92
  }
88
93
 
94
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
+ export interface GetItemsIsArrayOptions<T = any> {
96
+ items: T[]
97
+ isArray: boolean
98
+ }
99
+
89
100
  //#endregion
@@ -0,0 +1,15 @@
1
+ import type { HookContext } from "@feathersjs/feathers";
2
+ import type { GetItemsIsArrayOptions } from "..";
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ export const getItemsIsArray = <T = any>(
6
+ context: HookContext
7
+ ): GetItemsIsArrayOptions<T> => {
8
+ let itemOrItems = context.type === "before" ? context.data : context.result;
9
+ itemOrItems = itemOrItems && context.method === "find" ? (itemOrItems.data || itemOrItems) : itemOrItems;
10
+ const isArray = Array.isArray(itemOrItems);
11
+ return {
12
+ items: (isArray) ? itemOrItems : [itemOrItems],
13
+ isArray
14
+ };
15
+ };
@@ -1,4 +1,4 @@
1
- import { HookContext, PaginationOptions } from "@feathersjs/feathers";
1
+ import type { HookContext, PaginationOptions } from "@feathersjs/feathers";
2
2
 
3
3
  export const getPaginate = (
4
4
  context: HookContext
@@ -1,4 +1,4 @@
1
- import { HookContext } from "@feathersjs/feathers";
1
+ import type { HookContext } from "@feathersjs/feathers";
2
2
  import { getPaginate } from "./getPaginate";
3
3
 
4
4
  export const isPaginated = (
@@ -1,4 +1,4 @@
1
- import { HookContext } from "@feathersjs/feathers";
1
+ import type { HookContext } from "@feathersjs/feathers";
2
2
  import { isMulti } from "..";
3
3
  import { isPaginated } from "./isPaginated";
4
4
 
package/.eslintignore DELETED
@@ -1,3 +0,0 @@
1
- node_modules
2
- dist
3
- coverage
package/.eslintrc.js DELETED
@@ -1,44 +0,0 @@
1
- module.exports = {
2
- root: true,
3
- env: {
4
- node: true,
5
- mocha: true
6
- },
7
- parser: "@typescript-eslint/parser",
8
- plugins: [
9
- "security",
10
- "@typescript-eslint"
11
- ],
12
- extends: [
13
- "eslint:recommended",
14
- "plugin:security/recommended",
15
- "plugin:@typescript-eslint/recommended"
16
- ],
17
- rules: {
18
- "quotes": ["warn", "double", "avoid-escape"],
19
- "indent": ["warn", 2, { "SwitchCase": 1 }],
20
- "semi": ["warn", "always"],
21
- "@typescript-eslint/no-unused-vars": "warn",
22
- "no-console": "off",
23
- "camelcase": "warn",
24
- "require-atomic-updates": "off",
25
- "prefer-destructuring": ["warn", {
26
- "array": false,
27
- "object": true
28
- }, {
29
- "enforceForRenamedProperties": false
30
- }],
31
- "security/detect-object-injection": "off",
32
- "object-curly-spacing": ["warn", "always"],
33
- "prefer-const": ["warn"]
34
- },
35
- overrides: [
36
- {
37
- "files": ["test/**/*.ts"],
38
- "rules": {
39
- "@typescript-eslint/ban-ts-comment": ["off"]
40
- }
41
- }
42
- ]
43
- };
44
-
package/.gitlab-ci.yml DELETED
@@ -1,11 +0,0 @@
1
- .feathers-utils:
2
- only:
3
- changes:
4
- - packages/feathers-utils/**/*
5
-
6
- test:feathers-utils:
7
- variables:
8
- DIR: packages/feathers-utils
9
- extends:
10
- - .feathers-utils
11
- - .test
package/.mocharc.js DELETED
@@ -1,11 +0,0 @@
1
- 'use strict';
2
- const path = require("path");
3
-
4
- module.exports = {
5
- extension: ["ts"],
6
- package: path.join(__dirname, "./package.json"),
7
- ui: "bdd",
8
- spec: [
9
- "./test/**/*.test.ts",
10
- ]
11
- };
package/.nycrc.json DELETED
@@ -1,22 +0,0 @@
1
- {
2
- "extends": "@istanbuljs/nyc-config-typescript",
3
- "cache": false,
4
- "check-coverage": false,
5
- "include": [
6
- "src/**/*.js",
7
- "src/**/*.ts"
8
- ],
9
- "exclude": [
10
- "src/types.ts",
11
- "src/index.ts"
12
- ],
13
- "reporter": [
14
- "html",
15
- "text",
16
- "text-summary",
17
- "lcov"
18
- ],
19
- "sourceMap": true,
20
- "all": true,
21
- "instrument": true
22
- }
package/index.js DELETED
@@ -1,9 +0,0 @@
1
- exports.mergeQuery = require("./src/mergeQuery");
2
- exports.mergeArrays = require("./src/mergeQuery/mergeArrays");
3
- exports.shouldSkip = require("./src/shouldSkip");
4
- exports.pushSet = require("./src/pushSet");
5
- exports.isMulti = require("./src/isMulti");
6
-
7
- exports.hooks = {
8
- checkMulti: require("./src/hooks/checkMulti")
9
- };
package/tsconfig-esm.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "dist/esm",
5
- "moduleResolution": "node",
6
- "target": "ES2020",
7
- "module": "ESNext"
8
- }
9
- }
package/tsconfig.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "allowSyntheticDefaultImports": true,
4
- "esModuleInterop": true,
5
- "outDir": "dist",
6
- "moduleResolution": "node",
7
- "target": "es2015",
8
- "module": "commonjs",
9
- "strict": true,
10
- "downlevelIteration": true,
11
- "sourceMap": false,
12
- "declaration": true
13
- },
14
- "include": ["src/**/*"],
15
- "exclude": ["node_modules", "**/*.test.js"]
16
- }
@@ -1,13 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "allowSyntheticDefaultImports": true,
4
- "esModuleInterop": true,
5
- "outDir": "dist",
6
- "moduleResolution": "node",
7
- "target": "es2018",
8
- "sourceMap": true,
9
- "allowJs": true
10
- },
11
- "include": ["src/**/*"],
12
- "exclude": ["node_modules", "**/*.test.js"]
13
- }