feathers-utils 1.9.5 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +5 -3
  2. package/dist/esm/hooks/createRelated.d.ts +3 -0
  3. package/dist/esm/hooks/createRelated.js +25 -0
  4. package/dist/esm/hooks/onDelete.d.ts +3 -0
  5. package/dist/esm/hooks/onDelete.js +39 -0
  6. package/dist/esm/hooks/removeRelated.d.ts +3 -0
  7. package/dist/esm/hooks/removeRelated.js +28 -0
  8. package/dist/esm/hooks/runPerItem.d.ts +1 -1
  9. package/dist/esm/hooks/setData.js +3 -3
  10. package/dist/esm/index.d.ts +3 -0
  11. package/dist/esm/index.js +3 -0
  12. package/dist/esm/mixins/debounce-mixin/DebouncedStore.d.ts +1 -1
  13. package/dist/esm/mixins/debounce-mixin/DebouncedStore.js +1 -1
  14. package/dist/esm/types.d.ts +19 -0
  15. package/dist/esm/utils/mergeQuery/index.js +7 -7
  16. package/dist/esm/utils/pushSet.d.ts +1 -1
  17. package/dist/esm/utils/pushSet.js +3 -3
  18. package/dist/hooks/createRelated.d.ts +3 -0
  19. package/dist/hooks/createRelated.js +38 -0
  20. package/dist/hooks/onDelete.d.ts +3 -0
  21. package/dist/hooks/onDelete.js +52 -0
  22. package/dist/hooks/removeRelated.d.ts +3 -0
  23. package/dist/hooks/removeRelated.js +41 -0
  24. package/dist/hooks/runPerItem.d.ts +1 -1
  25. package/dist/hooks/setData.js +8 -8
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.js +7 -1
  28. package/dist/mixins/debounce-mixin/DebouncedStore.d.ts +1 -1
  29. package/dist/mixins/debounce-mixin/DebouncedStore.js +2 -2
  30. package/dist/types.d.ts +19 -0
  31. package/dist/utils/mergeQuery/index.js +42 -42
  32. package/dist/utils/pushSet.d.ts +1 -1
  33. package/dist/utils/pushSet.js +6 -6
  34. package/package.json +18 -17
  35. package/src/hooks/createRelated.ts +44 -0
  36. package/src/hooks/onDelete.ts +55 -0
  37. package/src/hooks/removeRelated.ts +40 -0
  38. package/src/hooks/setData.ts +3 -3
  39. package/src/index.ts +4 -0
  40. package/src/mixins/debounce-mixin/DebouncedStore.ts +1 -1
  41. package/src/types.ts +23 -0
  42. package/src/utils/mergeQuery/index.ts +7 -7
  43. package/src/utils/pushSet.ts +3 -3
package/README.md CHANGED
@@ -19,9 +19,11 @@ npm i feathers-utils
19
19
 
20
20
  ### Hooks
21
21
 
22
- - `checkMulti()`: throws if the request is **multi** data, but the service has `allowsMulti(method)` returns `false`
23
- - `runPerItem`
24
- - `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
+ - `removeRelated`: simple remove related items from a hook. Basically `cascade` at feathers level.
25
+ - `runPerItem`: run a function for every item. Meant for `multi:true`.
26
+ - `setData`: map properties from `context` to `data`. Something like `userId: context.params.user.id`
25
27
 
26
28
  ### Mixins
27
29
 
@@ -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,25 @@
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
+ checkContext(context, "after", undefined, "createRelated");
9
+ const { items } = getItemsIsArray(context);
10
+ let dataToCreate = (await Promise.all(items.map(async (item) => data(item, context)))).filter(x => !!x);
11
+ if (createItemsInDataArraySeparately) {
12
+ dataToCreate = dataToCreate.flat();
13
+ }
14
+ if (!dataToCreate || dataToCreate.length <= 0) {
15
+ return context;
16
+ }
17
+ if (multi) {
18
+ await context.app.service(service).create(dataToCreate);
19
+ }
20
+ else {
21
+ await Promise.all(dataToCreate.map(async (item) => context.app.service(service).create(item)));
22
+ }
23
+ return context;
24
+ };
25
+ }
@@ -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,39 @@
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
+ checkContext(context, "after", "remove", "onDelete");
12
+ const { items } = getItemsIsArray(context);
13
+ let ids = items.map(x => x[keyHere]).filter(x => !!x);
14
+ ids = [...new Set(ids)];
15
+ if (!ids || ids.length <= 0) {
16
+ return context;
17
+ }
18
+ const params = {
19
+ query: {
20
+ [keyThere]: {
21
+ $in: ids
22
+ }
23
+ },
24
+ paginate: false
25
+ };
26
+ let promise;
27
+ if (onDelete === "cascade") {
28
+ promise = context.app.service(service).remove(null, params);
29
+ }
30
+ else if (onDelete === "set null") {
31
+ const data = { [keyThere]: null };
32
+ promise = context.app.service(service).patch(null, data, params);
33
+ }
34
+ if (blocking) {
35
+ await promise;
36
+ }
37
+ return context;
38
+ };
39
+ }
@@ -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,28 @@
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
+ checkContext(context, "after", "remove", "removeRelated");
9
+ const { items } = getItemsIsArray(context);
10
+ let ids = items.map(x => x[keyHere]).filter(x => !!x);
11
+ ids = [...new Set(ids)];
12
+ if (!ids || ids.length <= 0) {
13
+ return context;
14
+ }
15
+ const promise = context.app.service(service).remove(null, {
16
+ query: {
17
+ [keyThere]: {
18
+ $in: ids
19
+ }
20
+ },
21
+ paginate: false
22
+ });
23
+ if (blocking) {
24
+ await promise;
25
+ }
26
+ return context;
27
+ };
28
+ }
@@ -1,3 +1,3 @@
1
1
  import type { HookRunPerItemOptions, ReturnAsyncHook, Promisable } from "../types";
2
2
  import type { HookContext } from "@feathersjs/feathers";
3
- export declare const runPerItem: (actionPerItem: (item: any, context: HookContext) => Promisable<any>, _options?: HookRunPerItemOptions | undefined) => ReturnAsyncHook;
3
+ export declare const runPerItem: (actionPerItem: (item: any, context: HookContext) => Promisable<any>, _options?: HookRunPerItemOptions) => ReturnAsyncHook;
@@ -1,6 +1,6 @@
1
- import _get from "lodash/get";
2
- import _set from "lodash/set";
3
- import _has from "lodash/has";
1
+ import _get from "lodash/get.js";
2
+ import _set from "lodash/set.js";
3
+ import _has from "lodash/has.js";
4
4
  import { Forbidden } from "@feathersjs/errors";
5
5
  import { getItemsIsArray } from "../utils/getItemsIsArray";
6
6
  const defaultOptions = {
@@ -7,7 +7,9 @@ export declare const hooks: {
7
7
  runPerItem: (actionPerItem: (item: any, context: import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<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, DebouncedStore } from "./mixins/debounce-mixin";
13
15
  export declare const mixins: {
@@ -27,5 +29,6 @@ export { setResultEmpty } from "./utils/setResultEmpty";
27
29
  export { markHookForSkip } from "./utils/markHookForSkip";
28
30
  export { filterQuery } from "./utils/filterQuery";
29
31
  export { getItemsIsArray } from "./utils/getItemsIsArray";
32
+ export { onDelete } from "./hooks/onDelete";
30
33
  export { shouldSkip } from "./utils/shouldSkip";
31
34
  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 = {
@@ -28,5 +30,6 @@ export { setResultEmpty } from "./utils/setResultEmpty";
28
30
  export { markHookForSkip } from "./utils/markHookForSkip";
29
31
  export { filterQuery } from "./utils/filterQuery";
30
32
  export { getItemsIsArray } from "./utils/getItemsIsArray";
33
+ export { onDelete } from "./hooks/onDelete";
31
34
  export { shouldSkip } from "./utils/shouldSkip";
32
35
  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<{}> | 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,
@@ -24,6 +24,25 @@ export interface AddHookOptions {
24
24
  export interface HookRunPerItemOptions {
25
25
  wait?: boolean;
26
26
  }
27
+ export interface RemoveRelatedOptions<S = Record<string, any>> {
28
+ service: keyof S;
29
+ keyThere: string;
30
+ keyHere: string;
31
+ blocking?: boolean;
32
+ }
33
+ export interface CreateRelatedOptions<S = Record<string, any>> {
34
+ service: keyof S;
35
+ multi?: boolean;
36
+ data: (item: any, context: HookContext) => Promisable<Record<string, any>>;
37
+ createItemsInDataArraySeparately?: boolean;
38
+ }
39
+ export declare type OnDeleteAction = "cascade" | "set null";
40
+ export interface OnDeleteOptions {
41
+ keyThere: string;
42
+ keyHere: string;
43
+ onDelete: OnDeleteAction;
44
+ blocking?: boolean;
45
+ }
27
46
  export interface InitDebounceMixinOptions {
28
47
  default: Partial<DebouncedStoreOptions>;
29
48
  blacklist: string[];
@@ -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";
@@ -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);
@@ -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,38 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createRelated = void 0;
13
+ const feathers_hooks_common_1 = require("feathers-hooks-common");
14
+ const getItemsIsArray_1 = require("../utils/getItemsIsArray");
15
+ function createRelated({ service, multi = true, data, createItemsInDataArraySeparately = true }) {
16
+ if (!service || !data) {
17
+ throw "initialize hook 'createRelated' completely!";
18
+ }
19
+ return (context) => __awaiter(this, void 0, void 0, function* () {
20
+ (0, feathers_hooks_common_1.checkContext)(context, "after", undefined, "createRelated");
21
+ const { items } = (0, getItemsIsArray_1.getItemsIsArray)(context);
22
+ let dataToCreate = (yield Promise.all(items.map((item) => __awaiter(this, void 0, void 0, function* () { return data(item, context); })))).filter(x => !!x);
23
+ if (createItemsInDataArraySeparately) {
24
+ dataToCreate = dataToCreate.flat();
25
+ }
26
+ if (!dataToCreate || dataToCreate.length <= 0) {
27
+ return context;
28
+ }
29
+ if (multi) {
30
+ yield context.app.service(service).create(dataToCreate);
31
+ }
32
+ else {
33
+ yield Promise.all(dataToCreate.map((item) => __awaiter(this, void 0, void 0, function* () { return context.app.service(service).create(item); })));
34
+ }
35
+ return context;
36
+ });
37
+ }
38
+ exports.createRelated = createRelated;
@@ -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,52 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.onDelete = void 0;
13
+ const feathers_hooks_common_1 = require("feathers-hooks-common");
14
+ const getItemsIsArray_1 = require("../utils/getItemsIsArray");
15
+ function onDelete(service, { keyThere, keyHere = "id", onDelete = "cascade", blocking = true }) {
16
+ if (!service || !keyThere) {
17
+ throw "initialize hook 'removeRelated' completely!";
18
+ }
19
+ if (!["cascade", "set null"].includes(onDelete)) {
20
+ throw "onDelete must be 'cascade' or 'set null'";
21
+ }
22
+ return (context) => __awaiter(this, void 0, void 0, function* () {
23
+ (0, feathers_hooks_common_1.checkContext)(context, "after", "remove", "onDelete");
24
+ const { items } = (0, getItemsIsArray_1.getItemsIsArray)(context);
25
+ let ids = items.map(x => x[keyHere]).filter(x => !!x);
26
+ ids = [...new Set(ids)];
27
+ if (!ids || ids.length <= 0) {
28
+ return context;
29
+ }
30
+ const params = {
31
+ query: {
32
+ [keyThere]: {
33
+ $in: ids
34
+ }
35
+ },
36
+ paginate: false
37
+ };
38
+ let promise;
39
+ if (onDelete === "cascade") {
40
+ promise = context.app.service(service).remove(null, params);
41
+ }
42
+ else if (onDelete === "set null") {
43
+ const data = { [keyThere]: null };
44
+ promise = context.app.service(service).patch(null, data, params);
45
+ }
46
+ if (blocking) {
47
+ yield promise;
48
+ }
49
+ return context;
50
+ });
51
+ }
52
+ exports.onDelete = onDelete;
@@ -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,41 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.removeRelated = void 0;
13
+ const feathers_hooks_common_1 = require("feathers-hooks-common");
14
+ const getItemsIsArray_1 = require("../utils/getItemsIsArray");
15
+ function removeRelated({ service, keyThere, keyHere = "id", blocking = true }) {
16
+ if (!service || !keyThere) {
17
+ throw "initialize hook 'removeRelated' completely!";
18
+ }
19
+ return (context) => __awaiter(this, void 0, void 0, function* () {
20
+ (0, feathers_hooks_common_1.checkContext)(context, "after", "remove", "removeRelated");
21
+ const { items } = (0, getItemsIsArray_1.getItemsIsArray)(context);
22
+ let ids = items.map(x => x[keyHere]).filter(x => !!x);
23
+ ids = [...new Set(ids)];
24
+ if (!ids || ids.length <= 0) {
25
+ return context;
26
+ }
27
+ const promise = context.app.service(service).remove(null, {
28
+ query: {
29
+ [keyThere]: {
30
+ $in: ids
31
+ }
32
+ },
33
+ paginate: false
34
+ });
35
+ if (blocking) {
36
+ yield promise;
37
+ }
38
+ return context;
39
+ });
40
+ }
41
+ exports.removeRelated = removeRelated;
@@ -1,3 +1,3 @@
1
1
  import type { HookRunPerItemOptions, ReturnAsyncHook, Promisable } from "../types";
2
2
  import type { HookContext } from "@feathersjs/feathers";
3
- export declare const runPerItem: (actionPerItem: (item: any, context: HookContext) => Promisable<any>, _options?: HookRunPerItemOptions | undefined) => ReturnAsyncHook;
3
+ export declare const runPerItem: (actionPerItem: (item: any, context: HookContext) => Promisable<any>, _options?: HookRunPerItemOptions) => ReturnAsyncHook;
@@ -4,9 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.setData = void 0;
7
- const get_1 = __importDefault(require("lodash/get"));
8
- const set_1 = __importDefault(require("lodash/set"));
9
- const has_1 = __importDefault(require("lodash/has"));
7
+ const get_js_1 = __importDefault(require("lodash/get.js"));
8
+ const set_js_1 = __importDefault(require("lodash/set.js"));
9
+ const has_js_1 = __importDefault(require("lodash/has.js"));
10
10
  const errors_1 = require("@feathersjs/errors");
11
11
  const getItemsIsArray_1 = require("../utils/getItemsIsArray");
12
12
  const defaultOptions = {
@@ -18,16 +18,16 @@ function setData(from, to, _options) {
18
18
  return (context) => {
19
19
  var _a;
20
20
  const { items } = (0, getItemsIsArray_1.getItemsIsArray)(context);
21
- if (!(0, has_1.default)(context, from)) {
21
+ if (!(0, has_js_1.default)(context, from)) {
22
22
  if (!((_a = context.params) === null || _a === void 0 ? void 0 : _a.provider) || options.allowUndefined === true) {
23
23
  return context;
24
24
  }
25
- if (!options.overwrite && items.every((item) => (0, has_1.default)(item, to))) {
25
+ if (!options.overwrite && items.every((item) => (0, has_js_1.default)(item, to))) {
26
26
  return context;
27
27
  }
28
28
  throw new errors_1.Forbidden(`Expected field ${from.toString()} not available`);
29
29
  }
30
- const val = (0, get_1.default)(context, from);
30
+ const val = (0, get_js_1.default)(context, from);
31
31
  items.forEach((item) => {
32
32
  let overwrite;
33
33
  if (typeof options.overwrite === "function") {
@@ -36,10 +36,10 @@ function setData(from, to, _options) {
36
36
  else {
37
37
  overwrite = options.overwrite;
38
38
  }
39
- if (!overwrite && (0, has_1.default)(item, to)) {
39
+ if (!overwrite && (0, has_js_1.default)(item, to)) {
40
40
  return;
41
41
  }
42
- (0, set_1.default)(item, to, val);
42
+ (0, set_js_1.default)(item, to, val);
43
43
  });
44
44
  return context;
45
45
  };
package/dist/index.d.ts CHANGED
@@ -7,7 +7,9 @@ export declare const hooks: {
7
7
  runPerItem: (actionPerItem: (item: any, context: import("@feathersjs/feathers").HookContext<any, import("@feathersjs/feathers").Service<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, DebouncedStore } from "./mixins/debounce-mixin";
13
15
  export declare const mixins: {
@@ -27,5 +29,6 @@ export { setResultEmpty } from "./utils/setResultEmpty";
27
29
  export { markHookForSkip } from "./utils/markHookForSkip";
28
30
  export { filterQuery } from "./utils/filterQuery";
29
31
  export { getItemsIsArray } from "./utils/getItemsIsArray";
32
+ export { onDelete } from "./hooks/onDelete";
30
33
  export { shouldSkip } from "./utils/shouldSkip";
31
34
  export * from "./types";
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- 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;
17
+ exports.shouldSkip = exports.onDelete = 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.removeRelated = exports.setData = exports.createRelated = exports.checkMulti = exports.hooks = void 0;
18
18
  // hooks
19
19
  const checkMulti_1 = require("./hooks/checkMulti");
20
20
  Object.defineProperty(exports, "checkMulti", { enumerable: true, get: function () { return checkMulti_1.checkMulti; } });
@@ -27,6 +27,10 @@ exports.hooks = {
27
27
  setData: setData_1.setData,
28
28
  runPerItem: runPerItem_1.runPerItem
29
29
  };
30
+ var createRelated_1 = require("./hooks/createRelated");
31
+ Object.defineProperty(exports, "createRelated", { enumerable: true, get: function () { return createRelated_1.createRelated; } });
32
+ var removeRelated_1 = require("./hooks/removeRelated");
33
+ Object.defineProperty(exports, "removeRelated", { enumerable: true, get: function () { return removeRelated_1.removeRelated; } });
30
34
  const debounce_mixin_1 = require("./mixins/debounce-mixin");
31
35
  Object.defineProperty(exports, "debounceMixin", { enumerable: true, get: function () { return debounce_mixin_1.debounceMixin; } });
32
36
  Object.defineProperty(exports, "DebouncedStore", { enumerable: true, get: function () { return debounce_mixin_1.DebouncedStore; } });
@@ -56,6 +60,8 @@ var filterQuery_1 = require("./utils/filterQuery");
56
60
  Object.defineProperty(exports, "filterQuery", { enumerable: true, get: function () { return filterQuery_1.filterQuery; } });
57
61
  var getItemsIsArray_1 = require("./utils/getItemsIsArray");
58
62
  Object.defineProperty(exports, "getItemsIsArray", { enumerable: true, get: function () { return getItemsIsArray_1.getItemsIsArray; } });
63
+ var onDelete_1 = require("./hooks/onDelete");
64
+ Object.defineProperty(exports, "onDelete", { enumerable: true, get: function () { return onDelete_1.onDelete; } });
59
65
  var shouldSkip_1 = require("./utils/shouldSkip");
60
66
  Object.defineProperty(exports, "shouldSkip", { enumerable: true, get: function () { return shouldSkip_1.shouldSkip; } });
61
67
  __exportStar(require("./types"), exports);
@@ -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<{}> | 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;
@@ -13,7 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.DebouncedStore = exports.makeDefaultOptions = void 0;
16
- const debounce_1 = __importDefault(require("lodash/debounce"));
16
+ const debounce_js_1 = __importDefault(require("lodash/debounce.js"));
17
17
  const makeDefaultOptions = () => {
18
18
  return {
19
19
  leading: false,
@@ -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] = (0, debounce_1.default)((id, action) => {
55
+ this._queueById[id] = (0, debounce_js_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);
package/dist/types.d.ts CHANGED
@@ -24,6 +24,25 @@ export interface AddHookOptions {
24
24
  export interface HookRunPerItemOptions {
25
25
  wait?: boolean;
26
26
  }
27
+ export interface RemoveRelatedOptions<S = Record<string, any>> {
28
+ service: keyof S;
29
+ keyThere: string;
30
+ keyHere: string;
31
+ blocking?: boolean;
32
+ }
33
+ export interface CreateRelatedOptions<S = Record<string, any>> {
34
+ service: keyof S;
35
+ multi?: boolean;
36
+ data: (item: any, context: HookContext) => Promisable<Record<string, any>>;
37
+ createItemsInDataArraySeparately?: boolean;
38
+ }
39
+ export declare type OnDeleteAction = "cascade" | "set null";
40
+ export interface OnDeleteOptions {
41
+ keyThere: string;
42
+ keyHere: string;
43
+ onDelete: OnDeleteAction;
44
+ blocking?: boolean;
45
+ }
27
46
  export interface InitDebounceMixinOptions {
28
47
  default: Partial<DebouncedStoreOptions>;
29
48
  blacklist: string[];
@@ -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
@@ -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
- (0, merge_1.default)(targetFilters, sourceFilters);
268
+ (0, merge_js_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
- !(0, isEmpty_1.default)(targetQuery)) {
274
+ !(0, isEmpty_js_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 (0, get_1.default)(target, pathOneUp);
304
+ return (0, get_js_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 => (0, isEmpty_1.default)(x))) {
310
+ if (target.some(x => (0, isEmpty_js_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 => (0, isEmpty_1.default)(x))) {
321
+ if (target.every(x => (0, isEmpty_js_1.default)(x))) {
322
322
  return undefined;
323
323
  }
324
324
  else {
325
- target = target.filter(x => !(0, isEmpty_1.default)(x));
325
+ target = target.filter(x => !(0, isEmpty_js_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 (0, uniqWith_1.default)(target, isEqual_1.default);
333
+ return (0, uniqWith_js_1.default)(target, isEqual_js_1.default);
334
334
  }
@@ -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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feathers-utils",
3
- "version": "1.9.5",
3
+ "version": "1.11.0",
4
4
  "description": "Some utils for projects using '@feathersjs/feathers'",
5
5
  "author": "fratzinger",
6
6
  "repository": {
@@ -39,29 +39,30 @@
39
39
  "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
40
40
  },
41
41
  "dependencies": {
42
- "@feathersjs/adapter-commons": "^4.5.12",
43
- "@feathersjs/errors": "^4.5.12",
44
- "@feathersjs/feathers": "^4.5.12",
42
+ "@feathersjs/adapter-commons": "^4.5.15",
43
+ "@feathersjs/errors": "^4.5.15",
44
+ "@feathersjs/feathers": "^4.5.15",
45
+ "feathers-hooks-common": "^6.1.5",
45
46
  "lodash": "^4.17.21"
46
47
  },
47
48
  "devDependencies": {
48
49
  "@istanbuljs/nyc-config-typescript": "^1.0.2",
49
- "@types/lodash": "^4.14.179",
50
- "@types/mocha": "^9.1.0",
51
- "@types/node": "^17.0.21",
52
- "@typescript-eslint/eslint-plugin": "^5.13.0",
53
- "@typescript-eslint/parser": "^5.13.0",
50
+ "@types/lodash": "^4.14.184",
51
+ "@types/mocha": "^9.1.1",
52
+ "@types/node": "^18.7.14",
53
+ "@typescript-eslint/eslint-plugin": "^5.36.0",
54
+ "@typescript-eslint/parser": "^5.36.0",
54
55
  "cross-env": "^7.0.3",
55
- "eslint": "^8.10.0",
56
- "eslint-import-resolver-typescript": "^2.5.0",
57
- "eslint-plugin-import": "^2.25.4",
58
- "eslint-plugin-security": "^1.4.0",
56
+ "eslint": "^8.23.0",
57
+ "eslint-import-resolver-typescript": "^3.5.0",
58
+ "eslint-plugin-import": "^2.26.0",
59
+ "eslint-plugin-security": "^1.5.0",
59
60
  "feathers-memory": "^4.1.0",
60
- "mocha": "^9.2.1",
61
- "np": "^7.6.0",
61
+ "mocha": "^10.0.0",
62
+ "np": "^7.6.2",
62
63
  "nyc": "^15.1.0",
63
64
  "shx": "^0.3.4",
64
- "ts-node": "^10.5.0",
65
- "typescript": "^4.6.2"
65
+ "ts-node": "^10.9.1",
66
+ "typescript": "^4.8.2"
66
67
  }
67
68
  }
@@ -0,0 +1,44 @@
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
+ checkContext(context, "after", undefined, "createRelated");
17
+
18
+ const { items } = getItemsIsArray(context);
19
+
20
+ let dataToCreate = (await Promise.all(
21
+ items.map(async item => data(item, context))
22
+ )).filter(x => !!x);
23
+
24
+ if (createItemsInDataArraySeparately) {
25
+ dataToCreate = dataToCreate.flat();
26
+ }
27
+
28
+ if (!dataToCreate || dataToCreate.length <= 0) {
29
+ return context;
30
+ }
31
+
32
+ if (multi) {
33
+ await context.app.service(service as string).create(dataToCreate);
34
+ } else {
35
+ await Promise.all(
36
+ dataToCreate.map(async item =>
37
+ context.app.service(service as string).create(item)
38
+ )
39
+ );
40
+ }
41
+
42
+ return context;
43
+ };
44
+ }
@@ -0,0 +1,55 @@
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
+ checkContext(context, "after", "remove", "onDelete");
23
+
24
+ const { items } = getItemsIsArray(context);
25
+
26
+ let ids = items.map(x => x[keyHere]).filter(x => !!x);
27
+ ids = [...new Set(ids)];
28
+
29
+ if (!ids || ids.length <= 0) { return context; }
30
+
31
+ const params = {
32
+ query: {
33
+ [keyThere]: {
34
+ $in: ids
35
+ }
36
+ },
37
+ paginate: false
38
+ };
39
+
40
+ let promise;
41
+
42
+ if (onDelete === "cascade") {
43
+ promise = context.app.service(service as string).remove(null, params);
44
+ } else if (onDelete === "set null") {
45
+ const data = { [keyThere]: null };
46
+ promise = context.app.service(service as string).patch(null, data, params);
47
+ }
48
+
49
+ if (blocking) {
50
+ await promise;
51
+ }
52
+
53
+ return context;
54
+ };
55
+ }
@@ -0,0 +1,40 @@
1
+ import type { HookContext } 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
+ checkContext(context, "after", "remove", "removeRelated");
17
+
18
+ const { items } = getItemsIsArray(context);
19
+
20
+ let ids = items.map(x => x[keyHere]).filter(x => !!x);
21
+ ids = [...new Set(ids)];
22
+
23
+ if (!ids || ids.length <= 0) { return context; }
24
+
25
+ const promise: Promise<any> = context.app.service(service as string).remove(null, {
26
+ query: {
27
+ [keyThere]: {
28
+ $in: ids
29
+ }
30
+ },
31
+ paginate: false
32
+ });
33
+
34
+ if (blocking) {
35
+ await promise;
36
+ }
37
+
38
+ return context;
39
+ };
40
+ }
@@ -1,6 +1,6 @@
1
- import _get from "lodash/get";
2
- import _set from "lodash/set";
3
- import _has from "lodash/has";
1
+ import _get from "lodash/get.js";
2
+ import _set from "lodash/set.js";
3
+ import _has from "lodash/has.js";
4
4
 
5
5
  import { Forbidden } from "@feathersjs/errors";
6
6
  import { getItemsIsArray } from "../utils/getItemsIsArray";
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  // hooks
2
2
  import { checkMulti } from "./hooks/checkMulti";
3
3
  import { setData } from "./hooks/setData";
4
+
4
5
  import { runPerItem } from "./hooks/runPerItem";
5
6
 
6
7
  export const hooks = {
@@ -10,7 +11,9 @@ export const hooks = {
10
11
  };
11
12
 
12
13
  export { checkMulti };
14
+ export { createRelated } from "./hooks/createRelated";
13
15
  export { setData };
16
+ export { removeRelated } from "./hooks/removeRelated";
14
17
  export { runPerItem };
15
18
 
16
19
  import { debounceMixin, DebouncedStore } from "./mixins/debounce-mixin";
@@ -35,6 +38,7 @@ export { setResultEmpty } from "./utils/setResultEmpty";
35
38
  export { markHookForSkip } from "./utils/markHookForSkip";
36
39
  export { filterQuery } from "./utils/filterQuery";
37
40
  export { getItemsIsArray } from "./utils/getItemsIsArray";
41
+ export { onDelete } from "./hooks/onDelete";
38
42
  export { shouldSkip } from "./utils/shouldSkip";
39
43
 
40
44
 
@@ -1,4 +1,4 @@
1
- import _debounce from "lodash/debounce";
1
+ import _debounce from "lodash/debounce.js";
2
2
 
3
3
  import type { DebouncedFunc } from "lodash";
4
4
  import type { Application, Id } from "@feathersjs/feathers";
package/src/types.ts CHANGED
@@ -36,6 +36,29 @@ export interface HookRunPerItemOptions {
36
36
  wait?: boolean
37
37
  }
38
38
 
39
+ export interface RemoveRelatedOptions<S = Record<string, any>> {
40
+ service: keyof S
41
+ keyThere: string
42
+ keyHere: string
43
+ blocking?: boolean
44
+ }
45
+
46
+ export interface CreateRelatedOptions<S = Record<string, any>> {
47
+ service: keyof S
48
+ multi?: boolean
49
+ data: (item: any, context: HookContext) => Promisable<Record<string, any>>
50
+ createItemsInDataArraySeparately?: boolean
51
+ }
52
+
53
+ export type OnDeleteAction = "cascade" | "set null";
54
+
55
+ export interface OnDeleteOptions {
56
+ keyThere: string
57
+ keyHere: string
58
+ onDelete: OnDeleteAction
59
+ blocking?: boolean
60
+ }
61
+
39
62
  //#endregion
40
63
 
41
64
  //#region mixins
@@ -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
 
9
9
  import { mergeArrays } from "./mergeArrays";
10
10
  import { filterQuery } from "../filterQuery";
@@ -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
 
5
5
  import type {
6
6
  Path,