feathers-utils 6.0.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -729,37 +729,35 @@ function checkMulti() {
729
729
  };
730
730
  }
731
731
 
732
- function createRelated({
733
- service,
734
- multi = true,
735
- data,
736
- createItemsInDataArraySeparately = true
737
- }) {
738
- if (!service || !data) {
739
- throw "initialize hook 'createRelated' completely!";
740
- }
732
+ function createRelated(options) {
741
733
  return async (context) => {
742
734
  if (shouldSkip("createRelated", context)) {
743
735
  return context;
744
736
  }
745
737
  feathersHooksCommon.checkContext(context, "after", void 0, "createRelated");
746
738
  const { items } = getItemsIsArray(context);
747
- let dataToCreate = (await Promise.all(items.map(async (item) => data(item, context)))).filter((x) => !!x);
748
- if (createItemsInDataArraySeparately) {
749
- dataToCreate = dataToCreate.flat();
750
- }
751
- if (!dataToCreate || dataToCreate.length <= 0) {
752
- return context;
753
- }
754
- if (multi) {
755
- await context.app.service(service).create(dataToCreate);
756
- } else {
757
- await Promise.all(
758
- dataToCreate.map(
759
- async (item) => context.app.service(service).create(item)
760
- )
761
- );
762
- }
739
+ const entries = Array.isArray(options) ? options : [options];
740
+ await Promise.all(
741
+ entries.map(async (entry) => {
742
+ const { data, service, createItemsInDataArraySeparately, multi } = entry;
743
+ let dataToCreate = (await Promise.all(items.map(async (item) => data(item, context)))).filter((x) => !!x);
744
+ if (createItemsInDataArraySeparately) {
745
+ dataToCreate = dataToCreate.flat();
746
+ }
747
+ if (!dataToCreate || dataToCreate.length <= 0) {
748
+ return context;
749
+ }
750
+ if (multi) {
751
+ await context.app.service(service).create(dataToCreate);
752
+ } else {
753
+ await Promise.all(
754
+ dataToCreate.map(
755
+ async (item) => context.app.service(service).create(item)
756
+ )
757
+ );
758
+ }
759
+ })
760
+ );
763
761
  return context;
764
762
  };
765
763
  }
@@ -797,46 +795,44 @@ const forEach = (actionPerItem, options) => {
797
795
  };
798
796
  };
799
797
 
800
- function onDelete(service, {
801
- keyThere,
802
- keyHere = "id",
803
- onDelete: onDelete2 = "cascade",
804
- blocking = true
805
- }) {
806
- if (!service || !keyThere) {
807
- throw "initialize hook 'removeRelated' completely!";
808
- }
809
- if (!["cascade", "set null"].includes(onDelete2)) {
810
- throw "onDelete must be 'cascade' or 'set null'";
811
- }
798
+ function onDelete(options) {
812
799
  return async (context) => {
813
800
  if (shouldSkip("onDelete", context)) {
814
801
  return context;
815
802
  }
816
803
  feathersHooksCommon.checkContext(context, "after", "remove", "onDelete");
817
804
  const { items } = getItemsIsArray(context);
818
- let ids = items.map((x) => x[keyHere]).filter((x) => !!x);
819
- ids = [...new Set(ids)];
820
- if (!ids || ids.length <= 0) {
821
- return context;
822
- }
823
- const params = {
824
- query: {
825
- [keyThere]: {
826
- $in: ids
805
+ const entries = Array.isArray(options) ? options : [options];
806
+ const promises = [];
807
+ entries.forEach(
808
+ async ({ keyHere, keyThere, onDelete: onDelete2, service, blocking }) => {
809
+ let ids = items.map((x) => x[keyHere]).filter((x) => !!x);
810
+ ids = [...new Set(ids)];
811
+ if (!ids || ids.length <= 0) {
812
+ return context;
827
813
  }
828
- },
829
- paginate: false
830
- };
831
- let promise;
832
- if (onDelete2 === "cascade") {
833
- promise = context.app.service(service).remove(null, params);
834
- } else if (onDelete2 === "set null") {
835
- const data = { [keyThere]: null };
836
- promise = context.app.service(service).patch(null, data, params);
837
- }
838
- if (blocking) {
839
- await promise;
814
+ const params = {
815
+ query: {
816
+ [keyThere]: {
817
+ $in: ids
818
+ }
819
+ },
820
+ paginate: false
821
+ };
822
+ let promise = void 0;
823
+ if (onDelete2 === "cascade") {
824
+ promise = context.app.service(service).remove(null, params);
825
+ } else if (onDelete2 === "set null") {
826
+ const data = { [keyThere]: null };
827
+ promise = context.app.service(service).patch(null, data, params);
828
+ }
829
+ if (blocking) {
830
+ promises.push(promise);
831
+ }
832
+ }
833
+ );
834
+ if (promises.length) {
835
+ await Promise.all(promises);
840
836
  }
841
837
  return context;
842
838
  };
package/dist/index.d.cts CHANGED
@@ -25,7 +25,7 @@ interface CreateRelatedOptions<S = Record<string, any>> {
25
25
  /**
26
26
  * hook to create related items
27
27
  */
28
- declare function createRelated<S = Record<string, any>, H extends HookContext = HookContext>({ service, multi, data, createItemsInDataArraySeparately, }: CreateRelatedOptions<S>): (context: H) => Promise<H>;
28
+ declare function createRelated<S = Record<string, any>, H extends HookContext = HookContext>(options: MaybeArray<CreateRelatedOptions<S>>): (context: H) => Promise<H>;
29
29
 
30
30
  type GetItemsIsArrayFrom = "data" | "result" | "automatic";
31
31
  type GetItemsIsArrayOptions = {
@@ -35,7 +35,7 @@ interface GetItemsIsArrayResult<T = any> {
35
35
  items: T[];
36
36
  isArray: boolean;
37
37
  }
38
- declare const getItemsIsArray: <T = any, H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H, options?: GetItemsIsArrayOptions) => GetItemsIsArrayResult<T>;
38
+ declare const getItemsIsArray: <T = any, H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H, options?: GetItemsIsArrayOptions) => GetItemsIsArrayResult<T>;
39
39
 
40
40
  type HookForEachOptions<T = any, H = HookContext, R = any> = {
41
41
  wait?: "sequential" | "parallel" | false;
@@ -49,10 +49,11 @@ type ActionPerItem<T, H, R> = (item: T, options: {
49
49
  } & (undefined extends R ? {} : {
50
50
  fromAll: R;
51
51
  })) => Promisable<any>;
52
- declare const forEach: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any, R = any>(actionPerItem: ActionPerItem<T, H, R>, options?: HookForEachOptions<T, H, R>) => ReturnAsyncHook<H>;
52
+ declare const forEach: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any, R = any>(actionPerItem: ActionPerItem<T, H, R>, options?: HookForEachOptions<T, H, R>) => ReturnAsyncHook<H>;
53
53
 
54
54
  type OnDeleteAction = "cascade" | "set null";
55
- interface OnDeleteOptions {
55
+ interface OnDeleteOptions<Path extends string = string> {
56
+ service: Path;
56
57
  keyThere: string;
57
58
  keyHere: string;
58
59
  onDelete: OnDeleteAction;
@@ -61,7 +62,7 @@ interface OnDeleteOptions {
61
62
  /**
62
63
  * hook to manipulate related items on delete
63
64
  */
64
- declare function onDelete<S = Record<string, any>, H extends HookContext = HookContext>(service: keyof S, { keyThere, keyHere, onDelete, blocking, }: OnDeleteOptions): (context: H) => Promise<H>;
65
+ declare function onDelete<S = Record<string, any>, H extends HookContext = HookContext>(options: MaybeArray<OnDeleteOptions<KeyOf<S>>>): (context: H) => Promise<H>;
65
66
 
66
67
  /**
67
68
  * Parse fields to date or number
@@ -79,6 +80,8 @@ interface RemoveRelatedOptions<S = Record<string, any>> {
79
80
  }
80
81
  /**
81
82
  * hook to remove related items
83
+ *
84
+ * @deprecated use 'onDelete' instead
82
85
  */
83
86
  declare function removeRelated<S = Record<string, any>, H extends HookContext = HookContext>({ service, keyThere, keyHere, blocking, }: RemoveRelatedOptions<S>): (context: H) => Promise<H>;
84
87
 
@@ -89,7 +92,7 @@ interface HookRunPerItemOptions {
89
92
  * hook to run a hook for each item in the context
90
93
  * uses `context.result` if it is existent. otherwise uses context.data
91
94
  */
92
- declare const runPerItem: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any>(actionPerItem: (item: T, context: H) => Promisable<any>, _options?: HookRunPerItemOptions) => (context: H) => Promise<H>;
95
+ declare const runPerItem: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any>(actionPerItem: (item: T, context: H) => Promisable<any>, _options?: HookRunPerItemOptions) => (context: H) => Promise<H>;
93
96
 
94
97
  type Predicate<T = any> = (item: T) => boolean;
95
98
  type PredicateWithContext<T = any> = (item: T, context: HookContext) => boolean;
@@ -103,14 +106,14 @@ interface HookSetDataOptions {
103
106
  */
104
107
  declare function setData<H extends HookContext = HookContext>(from: PropertyPath, to: PropertyPath, _options?: HookSetDataOptions): (context: H) => H;
105
108
 
106
- declare function defineParamsForServer(keyToHide: string): (...whitelist: string[]) => <H extends HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => H;
109
+ declare function defineParamsForServer(keyToHide: string): (...whitelist: string[]) => <H extends HookContext>(context: H) => H;
107
110
  /**
108
111
  * a hook to move params to query._$client
109
112
  * the server only receives 'query' from params. All other params are ignored.
110
113
  * So, to use `$populateParams` on the server, we need to move the params to query._$client
111
114
  * the server will move them back to params
112
115
  */
113
- declare const paramsForServer: (...whitelist: string[]) => <H extends HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => H;
116
+ declare const paramsForServer: (...whitelist: string[]) => <H extends HookContext>(context: H) => H;
114
117
 
115
118
  declare function defineParamsFromClient(keyToHide: string): (...whitelist: string[]) => (context: HookContext) => HookContext;
116
119
  declare const paramsFromClient: (...whitelist: string[]) => (context: HookContext) => HookContext;
@@ -176,7 +179,7 @@ declare function reassembleQuery(query: FilterQueryResult): Query;
176
179
  * 2. it uses `service.options.paginate` if it exists
177
180
  * 3. it uses `context.params.adapter` if it exists
178
181
  */
179
- declare const getPaginate: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => PaginationOptions | undefined;
182
+ declare const getPaginate: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => PaginationOptions | undefined;
180
183
 
181
184
  /**
182
185
  * util to check if a hook is a multi hook:
@@ -187,12 +190,12 @@ declare const getPaginate: <H extends HookContext<_feathersjs_feathers.Applicati
187
190
  * - patch: `context.id == null`
188
191
  * - remove: `context.id == null`
189
192
  */
190
- declare const isMulti: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => boolean;
193
+ declare const isMulti: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => boolean;
191
194
 
192
195
  /**
193
196
  * util to check if a hook is a paginated hook using `getPaginate`
194
197
  */
195
- declare const isPaginated: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => boolean;
198
+ declare const isPaginated: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => boolean;
196
199
 
197
200
  /**
198
201
  * util to mark a hook for skip, has to be used with `shouldSkip`
@@ -237,7 +240,7 @@ declare const setQueryKeySafely: (params: Params, key: string, value: any, opera
237
240
  /**
238
241
  * util to set `context.result` to an empty array or object, depending on the hook type
239
242
  */
240
- declare const setResultEmpty: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => H;
243
+ declare const setResultEmpty: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => H;
241
244
 
242
245
  type ShouldSkipOptions = {
243
246
  notSkippable?: boolean;
@@ -245,7 +248,7 @@ type ShouldSkipOptions = {
245
248
  /**
246
249
  * util to detect if a hook should be skipped
247
250
  */
248
- declare const shouldSkip: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(hookName: string, context: H, options?: ShouldSkipOptions) => boolean;
251
+ declare const shouldSkip: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(hookName: string, context: H, options?: ShouldSkipOptions) => boolean;
249
252
 
250
253
  declare const toJSON: (context: HookContext) => HookContext<_feathersjs_feathers.Application<any, any>, any>;
251
254
 
package/dist/index.d.mts CHANGED
@@ -25,7 +25,7 @@ interface CreateRelatedOptions<S = Record<string, any>> {
25
25
  /**
26
26
  * hook to create related items
27
27
  */
28
- declare function createRelated<S = Record<string, any>, H extends HookContext = HookContext>({ service, multi, data, createItemsInDataArraySeparately, }: CreateRelatedOptions<S>): (context: H) => Promise<H>;
28
+ declare function createRelated<S = Record<string, any>, H extends HookContext = HookContext>(options: MaybeArray<CreateRelatedOptions<S>>): (context: H) => Promise<H>;
29
29
 
30
30
  type GetItemsIsArrayFrom = "data" | "result" | "automatic";
31
31
  type GetItemsIsArrayOptions = {
@@ -35,7 +35,7 @@ interface GetItemsIsArrayResult<T = any> {
35
35
  items: T[];
36
36
  isArray: boolean;
37
37
  }
38
- declare const getItemsIsArray: <T = any, H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H, options?: GetItemsIsArrayOptions) => GetItemsIsArrayResult<T>;
38
+ declare const getItemsIsArray: <T = any, H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H, options?: GetItemsIsArrayOptions) => GetItemsIsArrayResult<T>;
39
39
 
40
40
  type HookForEachOptions<T = any, H = HookContext, R = any> = {
41
41
  wait?: "sequential" | "parallel" | false;
@@ -49,10 +49,11 @@ type ActionPerItem<T, H, R> = (item: T, options: {
49
49
  } & (undefined extends R ? {} : {
50
50
  fromAll: R;
51
51
  })) => Promisable<any>;
52
- declare const forEach: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any, R = any>(actionPerItem: ActionPerItem<T, H, R>, options?: HookForEachOptions<T, H, R>) => ReturnAsyncHook<H>;
52
+ declare const forEach: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any, R = any>(actionPerItem: ActionPerItem<T, H, R>, options?: HookForEachOptions<T, H, R>) => ReturnAsyncHook<H>;
53
53
 
54
54
  type OnDeleteAction = "cascade" | "set null";
55
- interface OnDeleteOptions {
55
+ interface OnDeleteOptions<Path extends string = string> {
56
+ service: Path;
56
57
  keyThere: string;
57
58
  keyHere: string;
58
59
  onDelete: OnDeleteAction;
@@ -61,7 +62,7 @@ interface OnDeleteOptions {
61
62
  /**
62
63
  * hook to manipulate related items on delete
63
64
  */
64
- declare function onDelete<S = Record<string, any>, H extends HookContext = HookContext>(service: keyof S, { keyThere, keyHere, onDelete, blocking, }: OnDeleteOptions): (context: H) => Promise<H>;
65
+ declare function onDelete<S = Record<string, any>, H extends HookContext = HookContext>(options: MaybeArray<OnDeleteOptions<KeyOf<S>>>): (context: H) => Promise<H>;
65
66
 
66
67
  /**
67
68
  * Parse fields to date or number
@@ -79,6 +80,8 @@ interface RemoveRelatedOptions<S = Record<string, any>> {
79
80
  }
80
81
  /**
81
82
  * hook to remove related items
83
+ *
84
+ * @deprecated use 'onDelete' instead
82
85
  */
83
86
  declare function removeRelated<S = Record<string, any>, H extends HookContext = HookContext>({ service, keyThere, keyHere, blocking, }: RemoveRelatedOptions<S>): (context: H) => Promise<H>;
84
87
 
@@ -89,7 +92,7 @@ interface HookRunPerItemOptions {
89
92
  * hook to run a hook for each item in the context
90
93
  * uses `context.result` if it is existent. otherwise uses context.data
91
94
  */
92
- declare const runPerItem: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any>(actionPerItem: (item: T, context: H) => Promisable<any>, _options?: HookRunPerItemOptions) => (context: H) => Promise<H>;
95
+ declare const runPerItem: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any>(actionPerItem: (item: T, context: H) => Promisable<any>, _options?: HookRunPerItemOptions) => (context: H) => Promise<H>;
93
96
 
94
97
  type Predicate<T = any> = (item: T) => boolean;
95
98
  type PredicateWithContext<T = any> = (item: T, context: HookContext) => boolean;
@@ -103,14 +106,14 @@ interface HookSetDataOptions {
103
106
  */
104
107
  declare function setData<H extends HookContext = HookContext>(from: PropertyPath, to: PropertyPath, _options?: HookSetDataOptions): (context: H) => H;
105
108
 
106
- declare function defineParamsForServer(keyToHide: string): (...whitelist: string[]) => <H extends HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => H;
109
+ declare function defineParamsForServer(keyToHide: string): (...whitelist: string[]) => <H extends HookContext>(context: H) => H;
107
110
  /**
108
111
  * a hook to move params to query._$client
109
112
  * the server only receives 'query' from params. All other params are ignored.
110
113
  * So, to use `$populateParams` on the server, we need to move the params to query._$client
111
114
  * the server will move them back to params
112
115
  */
113
- declare const paramsForServer: (...whitelist: string[]) => <H extends HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => H;
116
+ declare const paramsForServer: (...whitelist: string[]) => <H extends HookContext>(context: H) => H;
114
117
 
115
118
  declare function defineParamsFromClient(keyToHide: string): (...whitelist: string[]) => (context: HookContext) => HookContext;
116
119
  declare const paramsFromClient: (...whitelist: string[]) => (context: HookContext) => HookContext;
@@ -176,7 +179,7 @@ declare function reassembleQuery(query: FilterQueryResult): Query;
176
179
  * 2. it uses `service.options.paginate` if it exists
177
180
  * 3. it uses `context.params.adapter` if it exists
178
181
  */
179
- declare const getPaginate: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => PaginationOptions | undefined;
182
+ declare const getPaginate: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => PaginationOptions | undefined;
180
183
 
181
184
  /**
182
185
  * util to check if a hook is a multi hook:
@@ -187,12 +190,12 @@ declare const getPaginate: <H extends HookContext<_feathersjs_feathers.Applicati
187
190
  * - patch: `context.id == null`
188
191
  * - remove: `context.id == null`
189
192
  */
190
- declare const isMulti: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => boolean;
193
+ declare const isMulti: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => boolean;
191
194
 
192
195
  /**
193
196
  * util to check if a hook is a paginated hook using `getPaginate`
194
197
  */
195
- declare const isPaginated: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => boolean;
198
+ declare const isPaginated: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => boolean;
196
199
 
197
200
  /**
198
201
  * util to mark a hook for skip, has to be used with `shouldSkip`
@@ -237,7 +240,7 @@ declare const setQueryKeySafely: (params: Params, key: string, value: any, opera
237
240
  /**
238
241
  * util to set `context.result` to an empty array or object, depending on the hook type
239
242
  */
240
- declare const setResultEmpty: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => H;
243
+ declare const setResultEmpty: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => H;
241
244
 
242
245
  type ShouldSkipOptions = {
243
246
  notSkippable?: boolean;
@@ -245,7 +248,7 @@ type ShouldSkipOptions = {
245
248
  /**
246
249
  * util to detect if a hook should be skipped
247
250
  */
248
- declare const shouldSkip: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(hookName: string, context: H, options?: ShouldSkipOptions) => boolean;
251
+ declare const shouldSkip: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(hookName: string, context: H, options?: ShouldSkipOptions) => boolean;
249
252
 
250
253
  declare const toJSON: (context: HookContext) => HookContext<_feathersjs_feathers.Application<any, any>, any>;
251
254
 
package/dist/index.d.ts CHANGED
@@ -25,7 +25,7 @@ interface CreateRelatedOptions<S = Record<string, any>> {
25
25
  /**
26
26
  * hook to create related items
27
27
  */
28
- declare function createRelated<S = Record<string, any>, H extends HookContext = HookContext>({ service, multi, data, createItemsInDataArraySeparately, }: CreateRelatedOptions<S>): (context: H) => Promise<H>;
28
+ declare function createRelated<S = Record<string, any>, H extends HookContext = HookContext>(options: MaybeArray<CreateRelatedOptions<S>>): (context: H) => Promise<H>;
29
29
 
30
30
  type GetItemsIsArrayFrom = "data" | "result" | "automatic";
31
31
  type GetItemsIsArrayOptions = {
@@ -35,7 +35,7 @@ interface GetItemsIsArrayResult<T = any> {
35
35
  items: T[];
36
36
  isArray: boolean;
37
37
  }
38
- declare const getItemsIsArray: <T = any, H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H, options?: GetItemsIsArrayOptions) => GetItemsIsArrayResult<T>;
38
+ declare const getItemsIsArray: <T = any, H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H, options?: GetItemsIsArrayOptions) => GetItemsIsArrayResult<T>;
39
39
 
40
40
  type HookForEachOptions<T = any, H = HookContext, R = any> = {
41
41
  wait?: "sequential" | "parallel" | false;
@@ -49,10 +49,11 @@ type ActionPerItem<T, H, R> = (item: T, options: {
49
49
  } & (undefined extends R ? {} : {
50
50
  fromAll: R;
51
51
  })) => Promisable<any>;
52
- declare const forEach: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any, R = any>(actionPerItem: ActionPerItem<T, H, R>, options?: HookForEachOptions<T, H, R>) => ReturnAsyncHook<H>;
52
+ declare const forEach: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any, R = any>(actionPerItem: ActionPerItem<T, H, R>, options?: HookForEachOptions<T, H, R>) => ReturnAsyncHook<H>;
53
53
 
54
54
  type OnDeleteAction = "cascade" | "set null";
55
- interface OnDeleteOptions {
55
+ interface OnDeleteOptions<Path extends string = string> {
56
+ service: Path;
56
57
  keyThere: string;
57
58
  keyHere: string;
58
59
  onDelete: OnDeleteAction;
@@ -61,7 +62,7 @@ interface OnDeleteOptions {
61
62
  /**
62
63
  * hook to manipulate related items on delete
63
64
  */
64
- declare function onDelete<S = Record<string, any>, H extends HookContext = HookContext>(service: keyof S, { keyThere, keyHere, onDelete, blocking, }: OnDeleteOptions): (context: H) => Promise<H>;
65
+ declare function onDelete<S = Record<string, any>, H extends HookContext = HookContext>(options: MaybeArray<OnDeleteOptions<KeyOf<S>>>): (context: H) => Promise<H>;
65
66
 
66
67
  /**
67
68
  * Parse fields to date or number
@@ -79,6 +80,8 @@ interface RemoveRelatedOptions<S = Record<string, any>> {
79
80
  }
80
81
  /**
81
82
  * hook to remove related items
83
+ *
84
+ * @deprecated use 'onDelete' instead
82
85
  */
83
86
  declare function removeRelated<S = Record<string, any>, H extends HookContext = HookContext>({ service, keyThere, keyHere, blocking, }: RemoveRelatedOptions<S>): (context: H) => Promise<H>;
84
87
 
@@ -89,7 +92,7 @@ interface HookRunPerItemOptions {
89
92
  * hook to run a hook for each item in the context
90
93
  * uses `context.result` if it is existent. otherwise uses context.data
91
94
  */
92
- declare const runPerItem: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any>(actionPerItem: (item: T, context: H) => Promisable<any>, _options?: HookRunPerItemOptions) => (context: H) => Promise<H>;
95
+ declare const runPerItem: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any>(actionPerItem: (item: T, context: H) => Promisable<any>, _options?: HookRunPerItemOptions) => (context: H) => Promise<H>;
93
96
 
94
97
  type Predicate<T = any> = (item: T) => boolean;
95
98
  type PredicateWithContext<T = any> = (item: T, context: HookContext) => boolean;
@@ -103,14 +106,14 @@ interface HookSetDataOptions {
103
106
  */
104
107
  declare function setData<H extends HookContext = HookContext>(from: PropertyPath, to: PropertyPath, _options?: HookSetDataOptions): (context: H) => H;
105
108
 
106
- declare function defineParamsForServer(keyToHide: string): (...whitelist: string[]) => <H extends HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => H;
109
+ declare function defineParamsForServer(keyToHide: string): (...whitelist: string[]) => <H extends HookContext>(context: H) => H;
107
110
  /**
108
111
  * a hook to move params to query._$client
109
112
  * the server only receives 'query' from params. All other params are ignored.
110
113
  * So, to use `$populateParams` on the server, we need to move the params to query._$client
111
114
  * the server will move them back to params
112
115
  */
113
- declare const paramsForServer: (...whitelist: string[]) => <H extends HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => H;
116
+ declare const paramsForServer: (...whitelist: string[]) => <H extends HookContext>(context: H) => H;
114
117
 
115
118
  declare function defineParamsFromClient(keyToHide: string): (...whitelist: string[]) => (context: HookContext) => HookContext;
116
119
  declare const paramsFromClient: (...whitelist: string[]) => (context: HookContext) => HookContext;
@@ -176,7 +179,7 @@ declare function reassembleQuery(query: FilterQueryResult): Query;
176
179
  * 2. it uses `service.options.paginate` if it exists
177
180
  * 3. it uses `context.params.adapter` if it exists
178
181
  */
179
- declare const getPaginate: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => PaginationOptions | undefined;
182
+ declare const getPaginate: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => PaginationOptions | undefined;
180
183
 
181
184
  /**
182
185
  * util to check if a hook is a multi hook:
@@ -187,12 +190,12 @@ declare const getPaginate: <H extends HookContext<_feathersjs_feathers.Applicati
187
190
  * - patch: `context.id == null`
188
191
  * - remove: `context.id == null`
189
192
  */
190
- declare const isMulti: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => boolean;
193
+ declare const isMulti: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => boolean;
191
194
 
192
195
  /**
193
196
  * util to check if a hook is a paginated hook using `getPaginate`
194
197
  */
195
- declare const isPaginated: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => boolean;
198
+ declare const isPaginated: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => boolean;
196
199
 
197
200
  /**
198
201
  * util to mark a hook for skip, has to be used with `shouldSkip`
@@ -237,7 +240,7 @@ declare const setQueryKeySafely: (params: Params, key: string, value: any, opera
237
240
  /**
238
241
  * util to set `context.result` to an empty array or object, depending on the hook type
239
242
  */
240
- declare const setResultEmpty: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => H;
243
+ declare const setResultEmpty: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H) => H;
241
244
 
242
245
  type ShouldSkipOptions = {
243
246
  notSkippable?: boolean;
@@ -245,7 +248,7 @@ type ShouldSkipOptions = {
245
248
  /**
246
249
  * util to detect if a hook should be skipped
247
250
  */
248
- declare const shouldSkip: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(hookName: string, context: H, options?: ShouldSkipOptions) => boolean;
251
+ declare const shouldSkip: <H extends HookContext = HookContext<_feathersjs_feathers.Application<any, any>, any>>(hookName: string, context: H, options?: ShouldSkipOptions) => boolean;
249
252
 
250
253
  declare const toJSON: (context: HookContext) => HookContext<_feathersjs_feathers.Application<any, any>, any>;
251
254
 
package/dist/index.mjs CHANGED
@@ -718,37 +718,35 @@ function checkMulti() {
718
718
  };
719
719
  }
720
720
 
721
- function createRelated({
722
- service,
723
- multi = true,
724
- data,
725
- createItemsInDataArraySeparately = true
726
- }) {
727
- if (!service || !data) {
728
- throw "initialize hook 'createRelated' completely!";
729
- }
721
+ function createRelated(options) {
730
722
  return async (context) => {
731
723
  if (shouldSkip("createRelated", context)) {
732
724
  return context;
733
725
  }
734
726
  checkContext(context, "after", void 0, "createRelated");
735
727
  const { items } = getItemsIsArray(context);
736
- let dataToCreate = (await Promise.all(items.map(async (item) => data(item, context)))).filter((x) => !!x);
737
- if (createItemsInDataArraySeparately) {
738
- dataToCreate = dataToCreate.flat();
739
- }
740
- if (!dataToCreate || dataToCreate.length <= 0) {
741
- return context;
742
- }
743
- if (multi) {
744
- await context.app.service(service).create(dataToCreate);
745
- } else {
746
- await Promise.all(
747
- dataToCreate.map(
748
- async (item) => context.app.service(service).create(item)
749
- )
750
- );
751
- }
728
+ const entries = Array.isArray(options) ? options : [options];
729
+ await Promise.all(
730
+ entries.map(async (entry) => {
731
+ const { data, service, createItemsInDataArraySeparately, multi } = entry;
732
+ let dataToCreate = (await Promise.all(items.map(async (item) => data(item, context)))).filter((x) => !!x);
733
+ if (createItemsInDataArraySeparately) {
734
+ dataToCreate = dataToCreate.flat();
735
+ }
736
+ if (!dataToCreate || dataToCreate.length <= 0) {
737
+ return context;
738
+ }
739
+ if (multi) {
740
+ await context.app.service(service).create(dataToCreate);
741
+ } else {
742
+ await Promise.all(
743
+ dataToCreate.map(
744
+ async (item) => context.app.service(service).create(item)
745
+ )
746
+ );
747
+ }
748
+ })
749
+ );
752
750
  return context;
753
751
  };
754
752
  }
@@ -786,46 +784,44 @@ const forEach = (actionPerItem, options) => {
786
784
  };
787
785
  };
788
786
 
789
- function onDelete(service, {
790
- keyThere,
791
- keyHere = "id",
792
- onDelete: onDelete2 = "cascade",
793
- blocking = true
794
- }) {
795
- if (!service || !keyThere) {
796
- throw "initialize hook 'removeRelated' completely!";
797
- }
798
- if (!["cascade", "set null"].includes(onDelete2)) {
799
- throw "onDelete must be 'cascade' or 'set null'";
800
- }
787
+ function onDelete(options) {
801
788
  return async (context) => {
802
789
  if (shouldSkip("onDelete", context)) {
803
790
  return context;
804
791
  }
805
792
  checkContext(context, "after", "remove", "onDelete");
806
793
  const { items } = getItemsIsArray(context);
807
- let ids = items.map((x) => x[keyHere]).filter((x) => !!x);
808
- ids = [...new Set(ids)];
809
- if (!ids || ids.length <= 0) {
810
- return context;
811
- }
812
- const params = {
813
- query: {
814
- [keyThere]: {
815
- $in: ids
794
+ const entries = Array.isArray(options) ? options : [options];
795
+ const promises = [];
796
+ entries.forEach(
797
+ async ({ keyHere, keyThere, onDelete: onDelete2, service, blocking }) => {
798
+ let ids = items.map((x) => x[keyHere]).filter((x) => !!x);
799
+ ids = [...new Set(ids)];
800
+ if (!ids || ids.length <= 0) {
801
+ return context;
816
802
  }
817
- },
818
- paginate: false
819
- };
820
- let promise;
821
- if (onDelete2 === "cascade") {
822
- promise = context.app.service(service).remove(null, params);
823
- } else if (onDelete2 === "set null") {
824
- const data = { [keyThere]: null };
825
- promise = context.app.service(service).patch(null, data, params);
826
- }
827
- if (blocking) {
828
- await promise;
803
+ const params = {
804
+ query: {
805
+ [keyThere]: {
806
+ $in: ids
807
+ }
808
+ },
809
+ paginate: false
810
+ };
811
+ let promise = void 0;
812
+ if (onDelete2 === "cascade") {
813
+ promise = context.app.service(service).remove(null, params);
814
+ } else if (onDelete2 === "set null") {
815
+ const data = { [keyThere]: null };
816
+ promise = context.app.service(service).patch(null, data, params);
817
+ }
818
+ if (blocking) {
819
+ promises.push(promise);
820
+ }
821
+ }
822
+ );
823
+ if (promises.length) {
824
+ await Promise.all(promises);
829
825
  }
830
826
  return context;
831
827
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feathers-utils",
3
- "version": "6.0.0",
3
+ "version": "7.0.0",
4
4
  "description": "Some utils for projects using '@feathersjs/feathers'",
5
5
  "author": "fratzinger",
6
6
  "repository": {
@@ -30,43 +30,43 @@
30
30
  "lib/**",
31
31
  "dist/**"
32
32
  ],
33
- "scripts": {
34
- "build": "unbuild",
35
- "version": "npm run build",
36
- "release": "np",
37
- "test": "vitest run",
38
- "vitest": "vitest",
39
- "coverage": "vitest run --coverage",
40
- "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
41
- },
42
33
  "dependencies": {
43
- "@feathersjs/adapter-commons": "^5.0.13",
44
- "@feathersjs/errors": "^5.0.13",
45
- "@feathersjs/feathers": "^5.0.13",
34
+ "@feathersjs/adapter-commons": "^5.0.31",
35
+ "@feathersjs/errors": "^5.0.31",
36
+ "@feathersjs/feathers": "^5.0.31",
46
37
  "fast-equals": "^5.0.1",
47
- "feathers-hooks-common": "^8.1.1",
38
+ "feathers-hooks-common": "^8.2.1",
48
39
  "lodash": "^4.17.21"
49
40
  },
50
41
  "devDependencies": {
51
- "@feathersjs/memory": "^5.0.13",
52
- "@types/lodash": "^4.14.202",
53
- "@types/node": "^20.10.6",
42
+ "@feathersjs/memory": "^5.0.31",
43
+ "@types/lodash": "^4.17.13",
44
+ "@types/node": "^22.10.1",
54
45
  "@typescript-eslint/eslint-plugin": "^6.16.0",
55
46
  "@typescript-eslint/parser": "^6.16.0",
56
- "@vitest/coverage-v8": "^1.1.0",
47
+ "@vitest/coverage-v8": "^2.1.8",
57
48
  "eslint": "^8.56.0",
58
49
  "eslint-config-prettier": "^9.1.0",
59
- "eslint-import-resolver-typescript": "^3.6.1",
50
+ "eslint-import-resolver-typescript": "^3.7.0",
60
51
  "eslint-plugin-import": "^2.29.1",
61
52
  "eslint-plugin-prettier": "^5.1.2",
62
- "np": "^9.2.0",
63
- "prettier": "^3.1.1",
53
+ "np": "^10.1.0",
54
+ "prettier": "^3.4.1",
64
55
  "shx": "^0.3.4",
65
- "typescript": "^5.3.3",
56
+ "typescript": "^5.7.2",
66
57
  "unbuild": "^2.0.0",
67
- "vitest": "^1.1.0"
58
+ "vitest": "^2.1.8"
68
59
  },
69
60
  "peerDependencies": {
70
61
  "@feathersjs/feathers": "^5.0.0"
62
+ },
63
+ "scripts": {
64
+ "build": "unbuild",
65
+ "version": "npm run build",
66
+ "release": "np",
67
+ "test": "vitest run",
68
+ "vitest": "vitest",
69
+ "coverage": "vitest run --coverage",
70
+ "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
71
71
  }
72
- }
72
+ }
package/src/.DS_Store ADDED
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  import type { HookContext } from "@feathersjs/feathers";
2
2
  import { checkContext } from "feathers-hooks-common";
3
- import type { Promisable } from "../typesInternal";
3
+ import type { MaybeArray, Promisable } from "../typesInternal";
4
4
  import { getItemsIsArray, shouldSkip } from "../utils";
5
5
 
6
6
  export interface CreateRelatedOptions<S = Record<string, any>> {
@@ -16,15 +16,7 @@ export interface CreateRelatedOptions<S = Record<string, any>> {
16
16
  export function createRelated<
17
17
  S = Record<string, any>,
18
18
  H extends HookContext = HookContext,
19
- >({
20
- service,
21
- multi = true,
22
- data,
23
- createItemsInDataArraySeparately = true,
24
- }: CreateRelatedOptions<S>) {
25
- if (!service || !data) {
26
- throw "initialize hook 'createRelated' completely!";
27
- }
19
+ >(options: MaybeArray<CreateRelatedOptions<S>>) {
28
20
  return async (context: H) => {
29
21
  if (shouldSkip("createRelated", context)) {
30
22
  return context;
@@ -34,27 +26,36 @@ export function createRelated<
34
26
 
35
27
  const { items } = getItemsIsArray(context);
36
28
 
37
- let dataToCreate = (
38
- await Promise.all(items.map(async (item) => data(item, context)))
39
- ).filter((x) => !!x);
40
-
41
- if (createItemsInDataArraySeparately) {
42
- dataToCreate = dataToCreate.flat();
43
- }
44
-
45
- if (!dataToCreate || dataToCreate.length <= 0) {
46
- return context;
47
- }
48
-
49
- if (multi) {
50
- await context.app.service(service as string).create(dataToCreate);
51
- } else {
52
- await Promise.all(
53
- dataToCreate.map(async (item) =>
54
- context.app.service(service as string).create(item),
55
- ),
56
- );
57
- }
29
+ const entries = Array.isArray(options) ? options : [options];
30
+
31
+ await Promise.all(
32
+ entries.map(async (entry) => {
33
+ const { data, service, createItemsInDataArraySeparately, multi } =
34
+ entry;
35
+
36
+ let dataToCreate = (
37
+ await Promise.all(items.map(async (item) => data(item, context)))
38
+ ).filter((x) => !!x);
39
+
40
+ if (createItemsInDataArraySeparately) {
41
+ dataToCreate = dataToCreate.flat();
42
+ }
43
+
44
+ if (!dataToCreate || dataToCreate.length <= 0) {
45
+ return context;
46
+ }
47
+
48
+ if (multi) {
49
+ await context.app.service(service as string).create(dataToCreate);
50
+ } else {
51
+ await Promise.all(
52
+ dataToCreate.map(async (item) =>
53
+ context.app.service(service as string).create(item),
54
+ ),
55
+ );
56
+ }
57
+ }),
58
+ );
58
59
 
59
60
  return context;
60
61
  };
@@ -1,10 +1,12 @@
1
1
  import type { HookContext } from "@feathersjs/feathers";
2
2
  import { checkContext } from "feathers-hooks-common";
3
3
  import { getItemsIsArray, shouldSkip } from "../utils";
4
+ import type { KeyOf, MaybeArray } from "../typesInternal";
4
5
 
5
6
  export type OnDeleteAction = "cascade" | "set null";
6
7
 
7
- export interface OnDeleteOptions {
8
+ export interface OnDeleteOptions<Path extends string = string> {
9
+ service: Path;
8
10
  keyThere: string;
9
11
  keyHere: string;
10
12
  onDelete: OnDeleteAction;
@@ -17,22 +19,7 @@ export interface OnDeleteOptions {
17
19
  export function onDelete<
18
20
  S = Record<string, any>,
19
21
  H extends HookContext = HookContext,
20
- >(
21
- service: keyof S,
22
- {
23
- keyThere,
24
- keyHere = "id",
25
- onDelete = "cascade",
26
- blocking = true,
27
- }: OnDeleteOptions,
28
- ) {
29
- if (!service || !keyThere) {
30
- throw "initialize hook 'removeRelated' completely!";
31
- }
32
- if (!["cascade", "set null"].includes(onDelete)) {
33
- throw "onDelete must be 'cascade' or 'set null'";
34
- }
35
-
22
+ >(options: MaybeArray<OnDeleteOptions<KeyOf<S>>>) {
36
23
  return async (context: H) => {
37
24
  if (shouldSkip("onDelete", context)) {
38
25
  return context;
@@ -42,35 +29,47 @@ export function onDelete<
42
29
 
43
30
  const { items } = getItemsIsArray(context);
44
31
 
45
- let ids = items.map((x) => x[keyHere]).filter((x) => !!x);
46
- ids = [...new Set(ids)];
32
+ const entries = Array.isArray(options) ? options : [options];
47
33
 
48
- if (!ids || ids.length <= 0) {
49
- return context;
50
- }
34
+ const promises: Promise<any>[] = [];
51
35
 
52
- const params = {
53
- query: {
54
- [keyThere]: {
55
- $in: ids,
56
- },
57
- },
58
- paginate: false,
59
- };
36
+ entries.forEach(
37
+ async ({ keyHere, keyThere, onDelete, service, blocking }) => {
38
+ let ids = items.map((x) => x[keyHere]).filter((x) => !!x);
39
+ ids = [...new Set(ids)];
60
40
 
61
- let promise;
41
+ if (!ids || ids.length <= 0) {
42
+ return context;
43
+ }
62
44
 
63
- if (onDelete === "cascade") {
64
- promise = context.app.service(service as string).remove(null, params);
65
- } else if (onDelete === "set null") {
66
- const data = { [keyThere]: null };
67
- promise = context.app
68
- .service(service as string)
69
- .patch(null, data, params);
70
- }
45
+ const params = {
46
+ query: {
47
+ [keyThere]: {
48
+ $in: ids,
49
+ },
50
+ },
51
+ paginate: false,
52
+ };
53
+
54
+ let promise: Promise<any> | undefined = undefined;
55
+
56
+ if (onDelete === "cascade") {
57
+ promise = context.app.service(service as string).remove(null, params);
58
+ } else if (onDelete === "set null") {
59
+ const data = { [keyThere]: null };
60
+ promise = context.app
61
+ .service(service as string)
62
+ .patch(null, data, params);
63
+ }
64
+
65
+ if (blocking) {
66
+ promises.push(promise);
67
+ }
68
+ },
69
+ );
71
70
 
72
- if (blocking) {
73
- await promise;
71
+ if (promises.length) {
72
+ await Promise.all(promises);
74
73
  }
75
74
 
76
75
  return context;
@@ -11,6 +11,8 @@ export interface RemoveRelatedOptions<S = Record<string, any>> {
11
11
 
12
12
  /**
13
13
  * hook to remove related items
14
+ *
15
+ * @deprecated use 'onDelete' instead
14
16
  */
15
17
  export function removeRelated<
16
18
  S = Record<string, any>,