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
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,12 @@ 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
+ - `createRelated`: simply create related items from a hook.
24
+ - `onDelete`: simply remove/set null related items from a hook.
25
+ - `removeRelated`: simple remove related items from a hook. Basically `cascade` at feathers level.
26
+ - `runPerItem`: run a function for every item. Meant for `multi:true`.
27
+ - `setData`: map properties from `context` to `data`. Something like `userId: context.params.user.id`
24
28
 
25
29
  ### Mixins
26
30
 
@@ -28,11 +32,14 @@ npm i feathers-utils
28
32
 
29
33
  ### Utils
30
34
 
31
- - `addHook`: add hooks to specific services
32
35
  - `filterQuery`
36
+ - `getItemsIsArray(context)`: returns `{ items: any[], isArray: boolean }`
37
+ - `getPaginate`
33
38
  - `isMulti(context) => Boolean`: returns true, if `find`, `create/patch/remove`: multi
39
+ - `isPaginated`
34
40
  - `markHookForSkip`: add hookName to `context.params.skipHooks` - also see `shouldSkip`
35
41
  - `mergeQuery`: deeply merges queries
36
42
  - `mergeArrays`: merges arrays with intersection options
37
43
  - `pushSet`: if existing array: *push*, else *set*
44
+ - `setResultEmpty`
38
45
  - `shouldSkip`: checks `context.params.skipHooks` for `'all' | '${hookName}' | '${type}:${hookName}'` - also see `markHookForSkip`
@@ -0,0 +1,2 @@
1
+ import type { FilterQueryOptions } from "@feathersjs/adapter-commons";
2
+ export declare const filterArray: () => (arr: any, { operators }: FilterQueryOptions) => any;
@@ -0,0 +1,10 @@
1
+ import { validateQueryProperty } from "../utils/validateQueryProperty";
2
+ export const filterArray = () => (arr, { operators }) => {
3
+ if (arr && !Array.isArray(arr)) {
4
+ throw new Error("Invalid query parameter $and. It has to be an array");
5
+ }
6
+ if (Array.isArray(arr)) {
7
+ return arr.map((current) => validateQueryProperty(current, operators));
8
+ }
9
+ return arr;
10
+ };
@@ -1,2 +1,2 @@
1
- import type { HookContext } from "@feathersjs/feathers";
2
- export declare function checkMulti(): ((context: HookContext) => HookContext);
1
+ import type { ReturnSyncHook } from "../types";
2
+ export declare function checkMulti(): ReturnSyncHook;
@@ -0,0 +1,3 @@
1
+ import type { HookContext } from "@feathersjs/feathers";
2
+ import type { CreateRelatedOptions } from "../types";
3
+ export declare function createRelated<S = Record<string, any>>({ service, multi, data, createItemsInDataArraySeparately }: CreateRelatedOptions<S>): (context: HookContext) => Promise<HookContext>;
@@ -0,0 +1,26 @@
1
+ import { checkContext } from "feathers-hooks-common";
2
+ import { getItemsIsArray } from "../utils/getItemsIsArray";
3
+ export function createRelated({ service, multi = true, data, createItemsInDataArraySeparately = true }) {
4
+ if (!service || !data) {
5
+ throw "initialize hook 'createRelated' completely!";
6
+ }
7
+ return async (context) => {
8
+ // @ts-expect-error wait for feathers-hooks-common to update
9
+ checkContext(context, "after", undefined, "createRelated");
10
+ const { items } = getItemsIsArray(context);
11
+ let dataToCreate = (await Promise.all(items.map(async (item) => data(item, context)))).filter(x => !!x);
12
+ if (createItemsInDataArraySeparately) {
13
+ dataToCreate = dataToCreate.flat();
14
+ }
15
+ if (!dataToCreate || dataToCreate.length <= 0) {
16
+ return context;
17
+ }
18
+ if (multi) {
19
+ await context.app.service(service).create(dataToCreate);
20
+ }
21
+ else {
22
+ await Promise.all(dataToCreate.map(async (item) => context.app.service(service).create(item)));
23
+ }
24
+ return context;
25
+ };
26
+ }
@@ -0,0 +1,3 @@
1
+ import type { HookContext } from "@feathersjs/feathers";
2
+ import type { OnDeleteOptions } from "../types";
3
+ export declare function onDelete<S = Record<string, any>>(service: keyof S, { keyThere, keyHere, onDelete, blocking }: OnDeleteOptions): (context: HookContext) => Promise<HookContext>;
@@ -0,0 +1,40 @@
1
+ import { checkContext } from "feathers-hooks-common";
2
+ import { getItemsIsArray } from "../utils/getItemsIsArray";
3
+ export function onDelete(service, { keyThere, keyHere = "id", onDelete = "cascade", blocking = true }) {
4
+ if (!service || !keyThere) {
5
+ throw "initialize hook 'removeRelated' completely!";
6
+ }
7
+ if (!["cascade", "set null"].includes(onDelete)) {
8
+ throw "onDelete must be 'cascade' or 'set null'";
9
+ }
10
+ return async (context) => {
11
+ // @ts-expect-error wait for feathers-hooks-common to update
12
+ checkContext(context, "after", "remove", "onDelete");
13
+ const { items } = getItemsIsArray(context);
14
+ let ids = items.map(x => x[keyHere]).filter(x => !!x);
15
+ ids = [...new Set(ids)];
16
+ if (!ids || ids.length <= 0) {
17
+ return context;
18
+ }
19
+ const params = {
20
+ query: {
21
+ [keyThere]: {
22
+ $in: ids
23
+ }
24
+ },
25
+ paginate: false
26
+ };
27
+ let promise;
28
+ if (onDelete === "cascade") {
29
+ promise = context.app.service(service).remove(null, params);
30
+ }
31
+ else if (onDelete === "set null") {
32
+ const data = { [keyThere]: null };
33
+ promise = context.app.service(service).patch(null, data, params);
34
+ }
35
+ if (blocking) {
36
+ await promise;
37
+ }
38
+ return context;
39
+ };
40
+ }
@@ -0,0 +1,3 @@
1
+ import type { HookContext } from "@feathersjs/feathers";
2
+ import type { RemoveRelatedOptions } from "../types";
3
+ export declare function removeRelated<S = Record<string, any>>({ service, keyThere, keyHere, blocking }: RemoveRelatedOptions<S>): (context: HookContext) => Promise<HookContext>;
@@ -0,0 +1,30 @@
1
+ import { checkContext } from "feathers-hooks-common";
2
+ import { getItemsIsArray } from "../utils/getItemsIsArray";
3
+ export function removeRelated({ service, keyThere, keyHere = "id", blocking = true }) {
4
+ if (!service || !keyThere) {
5
+ throw "initialize hook 'removeRelated' completely!";
6
+ }
7
+ return async (context) => {
8
+ // @ts-expect-error wait for feathers-hooks-common to update
9
+ checkContext(context, "after", "remove", "removeRelated");
10
+ const { items } = getItemsIsArray(context);
11
+ let ids = items.map(x => x[keyHere]).filter(x => !!x);
12
+ ids = [...new Set(ids)];
13
+ if (!ids || ids.length <= 0) {
14
+ return context;
15
+ }
16
+ // feathers does not accept `paginate: false` for remove, but some adapters need it to work properly
17
+ const promise = context.app.service(service).remove(null, {
18
+ query: {
19
+ [keyThere]: {
20
+ $in: ids
21
+ }
22
+ },
23
+ paginate: false
24
+ });
25
+ if (blocking) {
26
+ await promise;
27
+ }
28
+ return context;
29
+ };
30
+ }
@@ -1,4 +1,3 @@
1
- import type { HookRunPerItemOptions } from "../types";
1
+ import type { HookRunPerItemOptions, ReturnAsyncHook, Promisable } from "../types";
2
2
  import type { HookContext } from "@feathersjs/feathers";
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>;
3
+ export declare const runPerItem: (actionPerItem: (item: any, context: HookContext) => Promisable<any>, _options?: HookRunPerItemOptions) => ReturnAsyncHook;
@@ -1,20 +1,20 @@
1
- import { getItems } from "feathers-hooks-common";
2
1
  import { shouldSkip } from "../utils/shouldSkip";
2
+ import { getItemsIsArray } from "../utils/getItemsIsArray";
3
3
  const makeOptions = (options) => {
4
4
  options = options || {};
5
5
  return Object.assign({
6
6
  wait: true
7
7
  }, options);
8
8
  };
9
- export const runPerItem = (actionPerItem, options) => {
10
- options = makeOptions(options);
9
+ export const runPerItem = (
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ actionPerItem, _options) => {
12
+ const options = makeOptions(_options);
11
13
  return async (context) => {
12
14
  if (shouldSkip("runForItems", context)) {
13
15
  return context;
14
16
  }
15
- //@ts-expect-error type error because feathers-hooks-common is feathers@4
16
- let items = getItems(context);
17
- items = (Array.isArray(items)) ? items : [items];
17
+ const { items } = getItemsIsArray(context);
18
18
  const promises = items.map(async (item) => {
19
19
  await actionPerItem(item, context);
20
20
  });
@@ -1,4 +1,3 @@
1
- import type { HookContext } from "@feathersjs/feathers";
2
- import type { HookSetDataOptions } from "../types";
3
1
  import type { PropertyPath } from "lodash";
4
- export declare function setData(from: PropertyPath, to: PropertyPath, _options?: HookSetDataOptions): ((context: HookContext) => HookContext);
2
+ import type { HookSetDataOptions, ReturnSyncHook } from "../types";
3
+ export declare function setData(from: PropertyPath, to: PropertyPath, _options?: HookSetDataOptions): ReturnSyncHook;
@@ -1,8 +1,8 @@
1
- import _get from "lodash/get";
2
- import _set from "lodash/set";
3
- import _has from "lodash/has";
4
- import { getItems } from "feathers-hooks-common";
1
+ import _get from "lodash/get.js";
2
+ import _set from "lodash/set.js";
3
+ import _has from "lodash/has.js";
5
4
  import { Forbidden } from "@feathersjs/errors";
5
+ import { getItemsIsArray } from "../utils/getItemsIsArray";
6
6
  const defaultOptions = {
7
7
  allowUndefined: false,
8
8
  overwrite: true
@@ -10,9 +10,7 @@ const defaultOptions = {
10
10
  export function setData(from, to, _options) {
11
11
  const options = Object.assign({}, defaultOptions, _options);
12
12
  return (context) => {
13
- //@ts-expect-error type error because feathers-hooks-common is feathers@4
14
- let items = getItems(context);
15
- items = (Array.isArray(items)) ? items : [items];
13
+ const { items } = getItemsIsArray(context);
16
14
  if (!_has(context, from)) {
17
15
  if (!context.params?.provider || options.allowUndefined === true) {
18
16
  return context;
@@ -24,7 +22,14 @@ export function setData(from, to, _options) {
24
22
  }
25
23
  const val = _get(context, from);
26
24
  items.forEach((item) => {
27
- if (!options.overwrite && _has(item, to)) {
25
+ let overwrite;
26
+ if (typeof options.overwrite === "function") {
27
+ overwrite = options.overwrite(item, context);
28
+ }
29
+ else {
30
+ overwrite = options.overwrite;
31
+ }
32
+ if (!overwrite && _has(item, to)) {
28
33
  return;
29
34
  }
30
35
  _set(item, to, val);
@@ -4,10 +4,12 @@ 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/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>>;
7
+ runPerItem: (actionPerItem: (item: any, context: import("@feathersjs/feathers/lib").HookContext<import("@feathersjs/feathers/lib").Application<any, any>, any>) => any, _options?: import("./types").HookRunPerItemOptions | undefined) => import("./types").ReturnAsyncHook;
8
8
  };
9
9
  export { checkMulti };
10
+ export { createRelated } from "./hooks/createRelated";
10
11
  export { setData };
12
+ export { removeRelated } from "./hooks/removeRelated";
11
13
  export { runPerItem };
12
14
  import { debounceMixin, DebouncedService, DebouncedStore } from "./mixins/debounce-mixin";
13
15
  export declare const mixins: {
@@ -25,6 +27,10 @@ export { mergeArrays } from "./utils/mergeQuery/mergeArrays";
25
27
  export { pushSet } from "./utils/pushSet";
26
28
  export { setResultEmpty } from "./utils/setResultEmpty";
27
29
  export { markHookForSkip } from "./utils/markHookForSkip";
28
- export { shouldSkip } from "./utils/shouldSkip";
29
30
  export { filterQuery } from "./utils/filterQuery";
31
+ export { getItemsIsArray } from "./utils/getItemsIsArray";
32
+ export { onDelete } from "./hooks/onDelete";
33
+ export { shouldSkip } from "./utils/shouldSkip";
34
+ export { validateQueryProperty } from "./utils/validateQueryProperty";
35
+ export { filterArray } from "./filters/array";
30
36
  export * from "./types";
package/dist/esm/index.js CHANGED
@@ -8,7 +8,9 @@ export const hooks = {
8
8
  runPerItem
9
9
  };
10
10
  export { checkMulti };
11
+ export { createRelated } from "./hooks/createRelated";
11
12
  export { setData };
13
+ export { removeRelated } from "./hooks/removeRelated";
12
14
  export { runPerItem };
13
15
  import { debounceMixin, DebouncedStore } from "./mixins/debounce-mixin";
14
16
  export const mixins = {
@@ -25,6 +27,11 @@ export { mergeArrays } from "./utils/mergeQuery/mergeArrays";
25
27
  export { pushSet } from "./utils/pushSet";
26
28
  export { setResultEmpty } from "./utils/setResultEmpty";
27
29
  export { markHookForSkip } from "./utils/markHookForSkip";
28
- export { shouldSkip } from "./utils/shouldSkip";
29
30
  export { filterQuery } from "./utils/filterQuery";
31
+ export { getItemsIsArray } from "./utils/getItemsIsArray";
32
+ export { onDelete } from "./hooks/onDelete";
33
+ export { shouldSkip } from "./utils/shouldSkip";
34
+ export { validateQueryProperty } from "./utils/validateQueryProperty";
35
+ // query filters
36
+ export { filterArray } from "./filters/array";
30
37
  export * from "./types";
@@ -7,7 +7,7 @@ export declare class DebouncedStore {
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<any, any> | undefined) => void | Promise<void>) => void | Promise<void> | undefined;
10
+ add: (id: Id, action: (app?: Application) => void | Promise<void>) => void | Promise<void> | undefined;
11
11
  constructor(app: Application, options?: Partial<DebouncedStoreOptions>);
12
12
  private unbounced;
13
13
  private debounceById;
@@ -1,4 +1,4 @@
1
- import _debounce from "lodash/debounce";
1
+ import _debounce from "lodash/debounce.js";
2
2
  export const makeDefaultOptions = () => {
3
3
  return {
4
4
  leading: false,
@@ -2,7 +2,7 @@ import { DebouncedStore } from "./DebouncedStore";
2
2
  import type { Application, FeathersService } from "@feathersjs/feathers";
3
3
  import type { InitDebounceMixinOptions } from "../../types";
4
4
  export declare type DebouncedService = FeathersService & {
5
- debouncedStore?: DebouncedStore;
5
+ debouncedStore: DebouncedStore;
6
6
  };
7
7
  export declare function debounceMixin(options?: Partial<InitDebounceMixinOptions>): ((app: Application) => void);
8
8
  export { DebouncedStore };
@@ -1,13 +1,19 @@
1
- import type { Application } from "@feathersjs/feathers";
2
- import type { AdapterService } from "@feathersjs/adapter-commons";
1
+ import type { Application, HookContext } from "@feathersjs/feathers";
2
+ import type { AdapterBase, FilterQueryOptions as PlainFilterQueryOptions } from "@feathersjs/adapter-commons";
3
3
  export declare type Path = Array<string | number>;
4
+ export declare type MaybeArray<T> = T | T[];
5
+ export declare type Promisable<T> = T | Promise<T>;
4
6
  export declare type HookType = "before" | "after" | "error";
5
7
  export declare type ServiceMethodName = "find" | "get" | "create" | "update" | "patch" | "remove";
8
+ export declare type ReturnSyncHook = (context: HookContext) => HookContext;
9
+ export declare type ReturnAsyncHook = (context: HookContext) => Promise<HookContext>;
6
10
  export declare type Handle = "target" | "source" | "combine" | "intersect" | "intersectOrFull";
7
11
  export declare type FirstLast = "first" | "last";
12
+ export declare type Predicate<T = any> = (item: T) => boolean;
13
+ export declare type PredicateWithContext<T = any> = (item: T, context: HookContext) => boolean;
8
14
  export interface HookSetDataOptions {
9
15
  allowUndefined?: boolean;
10
- overwrite?: boolean;
16
+ overwrite?: boolean | PredicateWithContext;
11
17
  }
12
18
  export interface AddHookOptions {
13
19
  types: HookType[];
@@ -19,6 +25,25 @@ export interface AddHookOptions {
19
25
  export interface HookRunPerItemOptions {
20
26
  wait?: boolean;
21
27
  }
28
+ export interface RemoveRelatedOptions<S = Record<string, any>> {
29
+ service: keyof S;
30
+ keyThere: string;
31
+ keyHere: string;
32
+ blocking?: boolean;
33
+ }
34
+ export interface CreateRelatedOptions<S = Record<string, any>> {
35
+ service: keyof S;
36
+ multi?: boolean;
37
+ data: (item: any, context: HookContext) => Promisable<Record<string, any>>;
38
+ createItemsInDataArraySeparately?: boolean;
39
+ }
40
+ export declare type OnDeleteAction = "cascade" | "set null";
41
+ export interface OnDeleteOptions {
42
+ keyThere: string;
43
+ keyHere: string;
44
+ onDelete: OnDeleteAction;
45
+ blocking?: boolean;
46
+ }
22
47
  export interface InitDebounceMixinOptions {
23
48
  default: Partial<DebouncedStoreOptions>;
24
49
  blacklist: string[];
@@ -44,17 +69,11 @@ export interface MergeQueryOptions<T> extends FilterQueryOptions<T> {
44
69
  };
45
70
  }
46
71
  export interface FilterQueryOptions<T> {
47
- service?: AdapterService<T>;
48
- operators?: string[];
49
- filters?: string[];
50
- }
51
- export interface PlainFilterQueryOptions {
52
- operators?: string[];
53
- filters?: string[];
54
- }
55
- export interface FilterQueryResult {
56
- filters: Record<string, unknown>;
57
- query: Record<string, unknown>;
58
- paginate?: unknown;
59
- [key: string]: unknown;
72
+ service?: AdapterBase<T>;
73
+ operators?: PlainFilterQueryOptions["operators"];
74
+ filters?: PlainFilterQueryOptions["filters"];
75
+ }
76
+ export interface GetItemsIsArrayOptions<T = any> {
77
+ items: T[];
78
+ isArray: boolean;
60
79
  }
@@ -1,3 +1,3 @@
1
- import type { FilterQueryOptions, FilterQueryResult } from "../types";
2
1
  import type { Query } from "@feathersjs/feathers";
3
- export declare function filterQuery<T>(query: Query, options?: FilterQueryOptions<T>): FilterQueryResult;
2
+ import type { FilterQueryOptions } from "../types";
3
+ export declare function filterQuery<T>(query: Query, _options?: FilterQueryOptions<T>): any;
@@ -1,12 +1,12 @@
1
- import { filterQuery as plainFilterQuery } from "@feathersjs/adapter-commons";
2
- export function filterQuery(query, options) {
1
+ import { filterQuery as plainFilterQuery, } from "@feathersjs/adapter-commons";
2
+ export function filterQuery(query, _options) {
3
3
  query = query || {};
4
- options = options || {};
5
- if (options?.service) {
6
- const { service } = options;
4
+ _options = _options || {};
5
+ const { service, ...options } = _options;
6
+ if (service) {
7
7
  const operators = options.operators
8
8
  ? options.operators
9
- : service.options?.whitelist;
9
+ : service.options?.operators;
10
10
  const filters = options.filters
11
11
  ? options.filters
12
12
  : service.options?.filters;
@@ -17,7 +17,9 @@ export function filterQuery(query, options) {
17
17
  if (filters) {
18
18
  optionsForFilterQuery.filters = filters;
19
19
  }
20
- if (typeof service?.filterQuery === "function") {
20
+ // @ts-expect-error service has no filterQuery method
21
+ if (service && "filterQuery" in service && typeof service.filterQuery === "function") {
22
+ // @ts-expect-error service has no filterQuery method
21
23
  return service.filterQuery({ query }, optionsForFilterQuery);
22
24
  }
23
25
  else {
@@ -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,18 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
+ export const getItemsIsArray = (context) => {
3
+ let itemOrItems = context.type === "before"
4
+ ? context.data
5
+ : context.result;
6
+ itemOrItems = itemOrItems && context.method === "find"
7
+ ? (itemOrItems.data || itemOrItems)
8
+ : itemOrItems;
9
+ const isArray = Array.isArray(itemOrItems);
10
+ return {
11
+ items: (isArray)
12
+ ? itemOrItems
13
+ : (itemOrItems != null)
14
+ ? [itemOrItems]
15
+ : [],
16
+ isArray
17
+ };
18
+ };
@@ -1,4 +1,4 @@
1
- import { HookContext } from "@feathersjs/feathers";
1
+ import type { HookContext } from "@feathersjs/feathers";
2
2
  interface PaginationOptions {
3
3
  default: number;
4
4
  max: number;
@@ -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,3 +1,3 @@
1
1
  import type { HookContext } from "@feathersjs/feathers";
2
- import type { HookType } from "feathers-hooks-common/types";
3
- export declare function markHookForSkip<T>(hookName: string, type: "all" | HookType | HookType[], context?: Partial<HookContext<T>>): Partial<HookContext<T>>;
2
+ import type { HookType, MaybeArray } from "../types";
3
+ export declare function markHookForSkip<T>(hookName: string, type: "all" | MaybeArray<HookType>, context?: Partial<HookContext<T>>): Partial<HookContext<T>>;
@@ -1,10 +1,10 @@
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";
1
+ import _get from "lodash/get.js";
2
+ import _has from "lodash/has.js";
3
+ import _isEmpty from "lodash/isEmpty.js";
4
+ import _isEqual from "lodash/isEqual.js";
5
+ import _merge from "lodash/merge.js";
6
+ import _set from "lodash/set.js";
7
+ import _uniqWith from "lodash/uniqWith.js";
8
8
  import { mergeArrays } from "./mergeArrays";
9
9
  import { filterQuery } from "../filterQuery";
10
10
  import { Forbidden } from "@feathersjs/errors";
@@ -227,12 +227,21 @@ function makeDefaultOptions(options) {
227
227
  }
228
228
  return options;
229
229
  }
230
+ function moveProperty(source, target, key) {
231
+ if (!Object.prototype.hasOwnProperty.call(source, key)) {
232
+ return;
233
+ }
234
+ target[key] = source[key];
235
+ delete source[key];
236
+ }
230
237
  export function mergeQuery(target, source, options) {
231
238
  const fullOptions = makeDefaultOptions(options);
232
239
  const { filters: targetFilters, query: targetQuery } = filterQuery(target, {
233
240
  operators: fullOptions.operators,
234
241
  service: fullOptions.service
235
242
  });
243
+ moveProperty(targetFilters, targetQuery, "$or");
244
+ moveProperty(targetFilters, targetQuery, "$and");
236
245
  if (target.$limit) {
237
246
  targetFilters.$limit = target.$limit;
238
247
  }
@@ -242,6 +251,8 @@ export function mergeQuery(target, source, options) {
242
251
  operators: fullOptions.operators,
243
252
  service: fullOptions.service
244
253
  });
254
+ moveProperty(sourceFilters, sourceQuery, "$or");
255
+ moveProperty(sourceFilters, sourceQuery, "$and");
245
256
  if (source.$limit) {
246
257
  sourceFilters.$limit = source.$limit;
247
258
  }
@@ -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[];
@@ -1,6 +1,6 @@
1
- import _isEqual from "lodash/isEqual";
2
- import _get from "lodash/get";
3
- import _set from "lodash/set";
1
+ import _isEqual from "lodash/isEqual.js";
2
+ import _get from "lodash/get.js";
3
+ import _set from "lodash/set.js";
4
4
  export const pushSet = (obj, path, val, options) => {
5
5
  options = options || {};
6
6
  let arr = _get(obj, path);
@@ -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,20 @@
1
+ import { _ } from "@feathersjs/commons";
2
+ import { BadRequest } from "@feathersjs/errors";
3
+ const isPlainObject = (value) => _.isObject(value) && value.constructor === {}.constructor;
4
+ export const validateQueryProperty = (query, operators = []) => {
5
+ if (!isPlainObject(query)) {
6
+ return query;
7
+ }
8
+ for (const key of Object.keys(query)) {
9
+ if (key.startsWith("$") && !operators.includes(key)) {
10
+ throw new BadRequest(`Invalid query parameter ${key}`, query);
11
+ }
12
+ const value = query[key];
13
+ if (isPlainObject(value)) {
14
+ query[key] = validateQueryProperty(value, operators);
15
+ }
16
+ }
17
+ return {
18
+ ...query
19
+ };
20
+ };
@@ -0,0 +1,2 @@
1
+ import type { FilterQueryOptions } from "@feathersjs/adapter-commons";
2
+ export declare const filterArray: () => (arr: any, { operators }: FilterQueryOptions) => any;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.filterArray = void 0;
4
+ const validateQueryProperty_1 = require("../utils/validateQueryProperty");
5
+ const filterArray = () => (arr, { operators }) => {
6
+ if (arr && !Array.isArray(arr)) {
7
+ throw new Error("Invalid query parameter $and. It has to be an array");
8
+ }
9
+ if (Array.isArray(arr)) {
10
+ return arr.map((current) => (0, validateQueryProperty_1.validateQueryProperty)(current, operators));
11
+ }
12
+ return arr;
13
+ };
14
+ exports.filterArray = filterArray;
@@ -1,2 +1,2 @@
1
- import type { HookContext } from "@feathersjs/feathers";
2
- export declare function checkMulti(): ((context: HookContext) => HookContext);
1
+ import type { ReturnSyncHook } from "../types";
2
+ export declare function checkMulti(): ReturnSyncHook;
@@ -0,0 +1,3 @@
1
+ import type { HookContext } from "@feathersjs/feathers";
2
+ import type { CreateRelatedOptions } from "../types";
3
+ export declare function createRelated<S = Record<string, any>>({ service, multi, data, createItemsInDataArraySeparately }: CreateRelatedOptions<S>): (context: HookContext) => Promise<HookContext>;