feathers-utils 6.0.0 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +3 -4
  2. package/dist/index.cjs +81 -99
  3. package/dist/index.d.cts +35 -33
  4. package/dist/index.d.mts +35 -33
  5. package/dist/index.d.ts +35 -33
  6. package/dist/index.mjs +81 -99
  7. package/package.json +29 -32
  8. package/src/.DS_Store +0 -0
  9. package/src/filters/array.ts +11 -13
  10. package/src/filters/index.ts +2 -2
  11. package/src/filters/object.ts +11 -11
  12. package/src/hooks/.DS_Store +0 -0
  13. package/src/hooks/checkMulti.ts +98 -82
  14. package/src/hooks/createRelated.ts +41 -41
  15. package/src/hooks/forEach.ts +32 -32
  16. package/src/hooks/from-client-for-server/common.ts +1 -1
  17. package/src/hooks/from-client-for-server/index.ts +2 -2
  18. package/src/hooks/from-client-for-server/paramsForServer.ts +32 -32
  19. package/src/hooks/from-client-for-server/paramsFromClient.ts +25 -25
  20. package/src/hooks/index.ts +9 -9
  21. package/src/hooks/onDelete.ts +54 -55
  22. package/src/hooks/parseFields.ts +13 -13
  23. package/src/hooks/removeRelated.ts +22 -20
  24. package/src/hooks/runPerItem.ts +17 -18
  25. package/src/hooks/setData.ts +295 -264
  26. package/src/index.ts +6 -6
  27. package/src/mixins/debounce-mixin/DebouncedStore.ts +29 -29
  28. package/src/mixins/debounce-mixin/debounceMixin.ts +17 -17
  29. package/src/mixins/debounce-mixin/index.ts +3 -3
  30. package/src/mixins/debounce-mixin/types.ts +9 -9
  31. package/src/mixins/debounce-mixin/utils.ts +3 -3
  32. package/src/mixins/index.ts +1 -1
  33. package/src/types.ts +3 -5
  34. package/src/typesInternal.ts +14 -14
  35. package/src/utility-types/index.ts +48 -48
  36. package/src/utils/_utils.internal.ts +5 -5
  37. package/src/utils/defineHooks.ts +8 -8
  38. package/src/utils/deflattenQuery.ts +31 -31
  39. package/src/utils/filterQuery.ts +58 -58
  40. package/src/utils/flattenQuery.ts +54 -54
  41. package/src/utils/getItemsIsArray.ts +148 -149
  42. package/src/utils/getPaginate.ts +31 -31
  43. package/src/utils/index.ts +17 -17
  44. package/src/utils/isMulti.ts +48 -40
  45. package/src/utils/isPaginated.ts +30 -30
  46. package/src/utils/markHookForSkip.ts +177 -178
  47. package/src/utils/mergeQuery/index.ts +3 -3
  48. package/src/utils/mergeQuery/mergeArrays.ts +67 -67
  49. package/src/utils/mergeQuery/mergeQuery.ts +211 -211
  50. package/src/utils/mergeQuery/types.ts +12 -12
  51. package/src/utils/mergeQuery/utils.ts +224 -224
  52. package/src/utils/optimizeBatchPatch.ts +42 -42
  53. package/src/utils/pushSet.ts +57 -57
  54. package/src/utils/setQueryKeySafely.ts +68 -68
  55. package/src/utils/setResultEmpty.ts +125 -123
  56. package/src/utils/shouldSkip.ts +72 -72
  57. package/src/utils/toJSON.ts +4 -4
  58. package/src/utils/validateQueryProperty.ts +10 -10
  59. package/src/hooks/makeSequelizeQuery.ts_ +0 -90
@@ -1,32 +1,32 @@
1
- import { MethodNotAllowed } from "@feathersjs/errors";
2
- import { shouldSkip, isMulti } from "../utils";
1
+ import { MethodNotAllowed } from '@feathersjs/errors'
2
+ import { shouldSkip, isMulti } from '../utils/index.js'
3
3
 
4
- import type { HookContext } from "@feathersjs/feathers";
4
+ import type { HookContext } from '@feathersjs/feathers'
5
5
 
6
6
  /**
7
7
  * hook to check if context is multi patch/remove and if the service allows it
8
8
  */
9
9
  export function checkMulti<H extends HookContext = HookContext>() {
10
10
  return (context: H) => {
11
- if (shouldSkip("checkMulti", context)) {
12
- return context;
11
+ if (shouldSkip('checkMulti', context)) {
12
+ return context
13
13
  }
14
14
 
15
- const { service, method } = context;
16
- if (!service.allowsMulti || !isMulti(context) || method === "find") {
17
- return context;
15
+ const { service, method } = context
16
+ if (!service.allowsMulti || !isMulti(context) || method === 'find') {
17
+ return context
18
18
  }
19
19
 
20
20
  if (!service.allowsMulti(method)) {
21
- throw new MethodNotAllowed(`Can not ${method} multiple entries`);
21
+ throw new MethodNotAllowed(`Can not ${method} multiple entries`)
22
22
  }
23
23
 
24
- return context;
25
- };
24
+ return context
25
+ }
26
26
  }
27
27
 
28
28
  if (import.meta.vitest) {
29
- const { it, assert } = import.meta.vitest;
29
+ const { it, assert } = import.meta.vitest
30
30
 
31
31
  it("passes if 'allowsMulti' not defined", function () {
32
32
  const makeContext = (type: string, method: string) =>
@@ -34,20 +34,20 @@ if (import.meta.vitest) {
34
34
  service: {},
35
35
  type,
36
36
  method,
37
- }) as HookContext;
38
- ["before", "after"].forEach((type) => {
39
- ["find", "get", "create", "update", "patch", "remove"].forEach(
37
+ }) as HookContext
38
+ ;['before', 'after'].forEach((type) => {
39
+ ;['find', 'get', 'create', 'update', 'patch', 'remove'].forEach(
40
40
  (method) => {
41
- const context = makeContext(type, method);
41
+ const context = makeContext(type, method)
42
42
 
43
43
  assert.doesNotThrow(
44
44
  () => checkMulti()(context),
45
45
  `'${type}:${method}': does not throw`,
46
- );
46
+ )
47
47
  },
48
- );
49
- });
50
- });
48
+ )
49
+ })
50
+ })
51
51
 
52
52
  it("passes if 'allowsMulti' returns true", function () {
53
53
  const makeContext = (type: string, method: string) => {
@@ -57,24 +57,28 @@ if (import.meta.vitest) {
57
57
  },
58
58
  method,
59
59
  type,
60
- } as HookContext;
61
- if (method === "create") {
62
- type === "before" ? (context.data = []) : (context.result = []);
60
+ } as HookContext
61
+ if (method === 'create') {
62
+ if (type === 'before') {
63
+ context.data = []
64
+ } else {
65
+ context.result = []
66
+ }
63
67
  }
64
- return context as HookContext;
65
- };
66
- ["before", "after"].forEach((type) => {
67
- ["find", "get", "create", "update", "patch", "remove"].forEach(
68
+ return context as HookContext
69
+ }
70
+ ;['before', 'after'].forEach((type) => {
71
+ ;['find', 'get', 'create', 'update', 'patch', 'remove'].forEach(
68
72
  (method) => {
69
- const context = makeContext(type, method);
73
+ const context = makeContext(type, method)
70
74
  assert.doesNotThrow(
71
75
  () => checkMulti()(context),
72
76
  `'${type}:${method}': does not throw`,
73
- );
77
+ )
74
78
  },
75
- );
76
- });
77
- });
79
+ )
80
+ })
81
+ })
78
82
 
79
83
  it("passes for 'find', 'get' and 'update'", function () {
80
84
  const makeContext = (type: string, method: string) =>
@@ -84,45 +88,49 @@ if (import.meta.vitest) {
84
88
  },
85
89
  type,
86
90
  method,
87
- }) as HookContext;
88
- ["before", "after"].forEach((type) => {
89
- ["find", "get", "update"].forEach((method) => {
90
- const context = makeContext(type, method);
91
+ }) as HookContext
92
+ ;['before', 'after'].forEach((type) => {
93
+ ;['find', 'get', 'update'].forEach((method) => {
94
+ const context = makeContext(type, method)
91
95
  assert.doesNotThrow(
92
96
  () => checkMulti()(context),
93
97
  `'${type}:${method}': does not throw`,
94
- );
95
- });
96
- });
97
- });
98
+ )
99
+ })
100
+ })
101
+ })
98
102
 
99
103
  it("passes if 'allowsMulti' returns false and is no multi data", function () {
100
104
  const makeContext = (type: string, method: string) => {
101
105
  const context = {
102
106
  service: {
103
- allowsMulti: (method: string) => method !== "find",
107
+ allowsMulti: (method: string) => method !== 'find',
104
108
  },
105
109
  method,
106
110
  type,
107
- } as HookContext;
108
- if (method === "create") {
109
- type === "before" ? (context.data = {}) : (context.result = {});
111
+ } as HookContext
112
+ if (method === 'create') {
113
+ if (type === 'before') {
114
+ context.data = {}
115
+ } else {
116
+ context.result = {}
117
+ }
110
118
  }
111
- if (["patch", "remove"].includes(method)) {
112
- context.id = 1;
119
+ if (['patch', 'remove'].includes(method)) {
120
+ context.id = 1
113
121
  }
114
- return context as HookContext;
115
- };
116
- ["before", "after"].forEach((type) => {
117
- ["create", "patch", "remove"].forEach((method) => {
118
- const context = makeContext(type, method);
122
+ return context as HookContext
123
+ }
124
+ ;['before', 'after'].forEach((type) => {
125
+ ;['create', 'patch', 'remove'].forEach((method) => {
126
+ const context = makeContext(type, method)
119
127
  assert.doesNotThrow(
120
128
  () => checkMulti()(context),
121
129
  `'${type}:${method}': does not throw`,
122
- );
123
- });
124
- });
125
- });
130
+ )
131
+ })
132
+ })
133
+ })
126
134
 
127
135
  it("throws if 'allowsMulti' returns false and multi data", function () {
128
136
  const makeContext = (type: string, method: string) => {
@@ -132,21 +140,25 @@ if (import.meta.vitest) {
132
140
  },
133
141
  method,
134
142
  type,
135
- } as HookContext;
136
- if (method === "create") {
137
- type === "before" ? (context.data = []) : (context.result = []);
143
+ } as HookContext
144
+ if (method === 'create') {
145
+ if (type === 'before') {
146
+ context.data = []
147
+ } else {
148
+ context.result = []
149
+ }
138
150
  }
139
- return context as HookContext;
140
- };
141
- ["before", "after"].forEach((type) => {
142
- ["create", "patch", "remove"].forEach((method) => {
143
- const context = makeContext(type, method);
144
- assert.throws(() => checkMulti()(context));
145
- });
146
- });
147
- });
151
+ return context as HookContext
152
+ }
153
+ ;['before', 'after'].forEach((type) => {
154
+ ;['create', 'patch', 'remove'].forEach((method) => {
155
+ const context = makeContext(type, method)
156
+ assert.throws(() => checkMulti()(context))
157
+ })
158
+ })
159
+ })
148
160
 
149
- it("can skip hook", function () {
161
+ it('can skip hook', function () {
150
162
  const makeContext = (type: string, method: string) => {
151
163
  const context = {
152
164
  service: {
@@ -155,23 +167,27 @@ if (import.meta.vitest) {
155
167
  method,
156
168
  type,
157
169
  params: {},
158
- } as HookContext;
159
- if (method === "create") {
160
- type === "before" ? (context.data = []) : (context.result = []);
170
+ } as HookContext
171
+ if (method === 'create') {
172
+ if (type === 'before') {
173
+ context.data = {}
174
+ } else {
175
+ context.result = {}
176
+ }
161
177
  }
162
- return context as HookContext;
163
- };
164
- ["before", "after"].forEach((type) => {
165
- ["create", "patch", "remove"].forEach((method) => {
166
- const context = makeContext(type, method);
178
+ return context as HookContext
179
+ }
180
+ ;['before', 'after'].forEach((type) => {
181
+ ;['create', 'patch', 'remove'].forEach((method) => {
182
+ const context = makeContext(type, method)
167
183
  context.params = {
168
- skipHooks: ["checkMulti"],
169
- };
184
+ skipHooks: ['checkMulti'],
185
+ }
170
186
  assert.doesNotThrow(
171
187
  () => checkMulti()(context),
172
188
  `'${type}:${method}': throws`,
173
- );
174
- });
175
- });
176
- });
189
+ )
190
+ })
191
+ })
192
+ })
177
193
  }
@@ -1,13 +1,13 @@
1
- import type { HookContext } from "@feathersjs/feathers";
2
- import { checkContext } from "feathers-hooks-common";
3
- import type { Promisable } from "../typesInternal";
4
- import { getItemsIsArray, shouldSkip } from "../utils";
1
+ import type { HookContext } from '@feathersjs/feathers'
2
+ import { checkContext } from 'feathers-hooks-common'
3
+ import type { MaybeArray, Promisable } from '../typesInternal.js'
4
+ import { getItemsIsArray, shouldSkip } from '../utils/index.js'
5
5
 
6
6
  export interface CreateRelatedOptions<S = Record<string, any>> {
7
- service: keyof S;
8
- multi?: boolean;
9
- data: (item: any, context: HookContext) => Promisable<Record<string, any>>;
10
- createItemsInDataArraySeparately?: boolean;
7
+ service: keyof S
8
+ multi?: boolean
9
+ data: (item: any, context: HookContext) => Promisable<Record<string, any>>
10
+ createItemsInDataArraySeparately?: boolean
11
11
  }
12
12
 
13
13
  /**
@@ -16,46 +16,46 @@ 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
- if (shouldSkip("createRelated", context)) {
30
- return context;
21
+ if (shouldSkip('createRelated', context)) {
22
+ return context
31
23
  }
32
24
 
33
- checkContext(context, "after", undefined, "createRelated");
25
+ checkContext(context, 'after', undefined, 'createRelated')
34
26
 
35
- const { items } = getItemsIsArray(context);
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);
29
+ const entries = Array.isArray(options) ? options : [options]
40
30
 
41
- if (createItemsInDataArraySeparately) {
42
- dataToCreate = dataToCreate.flat();
43
- }
31
+ await Promise.all(
32
+ entries.map(async (entry) => {
33
+ const { data, service, createItemsInDataArraySeparately, multi } = entry
44
34
 
45
- if (!dataToCreate || dataToCreate.length <= 0) {
46
- return context;
47
- }
35
+ let dataToCreate = (
36
+ await Promise.all(items.map(async (item) => data(item, context)))
37
+ ).filter((x) => !!x)
48
38
 
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
- }
39
+ if (createItemsInDataArraySeparately) {
40
+ dataToCreate = dataToCreate.flat()
41
+ }
58
42
 
59
- return context;
60
- };
43
+ if (!dataToCreate || dataToCreate.length <= 0) {
44
+ return context
45
+ }
46
+
47
+ if (multi) {
48
+ await context.app.service(service as string).create(dataToCreate)
49
+ } else {
50
+ await Promise.all(
51
+ dataToCreate.map(async (item) =>
52
+ context.app.service(service as string).create(item),
53
+ ),
54
+ )
55
+ }
56
+ }),
57
+ )
58
+
59
+ return context
60
+ }
61
61
  }
@@ -1,71 +1,71 @@
1
- import { shouldSkip } from "../utils/shouldSkip";
1
+ import { shouldSkip } from '../utils/shouldSkip.js'
2
2
 
3
- import type { ReturnAsyncHook, Promisable } from "../typesInternal";
4
- import type { HookContext } from "@feathersjs/feathers";
5
- import type { GetItemsIsArrayOptions } from "../utils/getItemsIsArray";
6
- import { getItemsIsArray } from "../utils/getItemsIsArray";
3
+ import type { ReturnAsyncHook, Promisable } from '../typesInternal.js'
4
+ import type { HookContext } from '@feathersjs/feathers'
5
+ import type { GetItemsIsArrayOptions } from '../utils/getItemsIsArray.js'
6
+ import { getItemsIsArray } from '../utils/getItemsIsArray.js'
7
7
 
8
8
  export type HookForEachOptions<T = any, H = HookContext, R = any> = {
9
- wait?: "sequential" | "parallel" | false;
10
- items?: GetItemsIsArrayOptions["from"];
11
- forAll?: (items: T[], context: H) => Promisable<R>;
12
- skip?: (context: H) => Promisable<boolean>;
13
- };
9
+ wait?: 'sequential' | 'parallel' | false
10
+ items?: GetItemsIsArrayOptions['from']
11
+ forAll?: (items: T[], context: H) => Promisable<R>
12
+ skip?: (context: H) => Promisable<boolean>
13
+ }
14
14
 
15
15
  type ActionPerItem<T, H, R> = (
16
16
  item: T,
17
17
  options: {
18
- context: H;
19
- i: number;
20
- // eslint-disable-next-line @typescript-eslint/ban-types
18
+ context: H
19
+ i: number
20
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
21
21
  } & (undefined extends R ? {} : { fromAll: R }),
22
- ) => Promisable<any>;
22
+ ) => Promisable<any>
23
23
 
24
24
  export const forEach = <H extends HookContext = HookContext, T = any, R = any>(
25
25
  actionPerItem: ActionPerItem<T, H, R>,
26
26
  options?: HookForEachOptions<T, H, R>,
27
27
  ): ReturnAsyncHook<H> => {
28
- const { wait = "parallel", items: from = "automatic" } = options || {};
28
+ const { wait = 'parallel', items: from = 'automatic' } = options || {}
29
29
 
30
30
  return async (context: H): Promise<H> => {
31
- if (shouldSkip("forEach", context)) {
32
- return context;
31
+ if (shouldSkip('forEach', context)) {
32
+ return context
33
33
  }
34
34
 
35
35
  if (options?.skip && (await options.skip(context))) {
36
- return context;
36
+ return context
37
37
  }
38
38
 
39
- const { items } = getItemsIsArray(context, { from });
39
+ const { items } = getItemsIsArray(context, { from })
40
40
 
41
41
  const forAll = options?.forAll
42
42
  ? await options.forAll(items, context)
43
- : ({} as R);
43
+ : ({} as R)
44
44
 
45
- const promises: Promise<any>[] = [];
45
+ const promises: Promise<any>[] = []
46
46
 
47
- let i = 0;
47
+ let i = 0
48
48
 
49
49
  for (const item of items) {
50
50
  const promise = actionPerItem(item, {
51
51
  context,
52
52
  i,
53
53
  ...(forAll ? { fromAll: forAll } : {}),
54
- } as any);
54
+ } as any)
55
55
 
56
- if (wait === "sequential") {
57
- await promise;
56
+ if (wait === 'sequential') {
57
+ await promise
58
58
  } else {
59
- promises.push(promise);
59
+ promises.push(promise)
60
60
  }
61
61
 
62
- i++;
62
+ i++
63
63
  }
64
64
 
65
- if (wait === "parallel") {
66
- await Promise.all(promises);
65
+ if (wait === 'parallel') {
66
+ await Promise.all(promises)
67
67
  }
68
68
 
69
- return context;
70
- };
71
- };
69
+ return context
70
+ }
71
+ }
@@ -1 +1 @@
1
- export const FROM_CLIENT_FOR_SERVER_DEFAULT_KEY = "_$client" as const;
1
+ export const FROM_CLIENT_FOR_SERVER_DEFAULT_KEY = '_$client' as const
@@ -1,2 +1,2 @@
1
- export * from "./paramsForServer";
2
- export * from "./paramsFromClient";
1
+ export * from './paramsForServer.js'
2
+ export * from './paramsFromClient.js'
@@ -1,43 +1,43 @@
1
- import type { HookContext } from "@feathersjs/feathers";
2
- import { FROM_CLIENT_FOR_SERVER_DEFAULT_KEY } from "./common";
1
+ import type { HookContext, Params, Query } from '@feathersjs/feathers'
2
+ import { FROM_CLIENT_FOR_SERVER_DEFAULT_KEY } from './common.js'
3
3
 
4
4
  export function defineParamsForServer(keyToHide: string) {
5
5
  return function paramsForServer(...whitelist: string[]) {
6
6
  return <H extends HookContext>(context: H) => {
7
7
  // clone params on demand
8
- let clonedParams;
8
+ let clonedParams: (Params & { query: Query }) | undefined
9
9
 
10
10
  Object.keys(context.params).forEach((key) => {
11
- if (key === "query") {
12
- return;
11
+ if (key === 'query') {
12
+ return
13
13
  }
14
14
 
15
15
  if (whitelist.includes(key)) {
16
- if (!clonedParams) {
17
- clonedParams = {
18
- ...context.params,
19
- query: {
20
- ...context.params.query,
21
- },
22
- };
16
+ const currentParams = clonedParams ?? {
17
+ ...context.params,
18
+ query: {
19
+ ...context.params.query,
20
+ },
23
21
  }
24
22
 
25
- if (!clonedParams.query[keyToHide]) {
26
- clonedParams.query[keyToHide] = {};
23
+ if (!currentParams.query[keyToHide]) {
24
+ currentParams.query[keyToHide] = {}
27
25
  }
28
26
 
29
- clonedParams.query[keyToHide][key] = clonedParams[key];
30
- delete clonedParams[key];
27
+ currentParams.query[keyToHide][key] = currentParams[key]
28
+ delete currentParams[key]
29
+
30
+ clonedParams = currentParams
31
31
  }
32
- });
32
+ })
33
33
 
34
34
  if (clonedParams) {
35
- context.params = clonedParams;
35
+ context.params = clonedParams
36
36
  }
37
37
 
38
- return context;
39
- };
40
- };
38
+ return context
39
+ }
40
+ }
41
41
  }
42
42
 
43
43
  /**
@@ -48,16 +48,16 @@ export function defineParamsForServer(keyToHide: string) {
48
48
  */
49
49
  export const paramsForServer = defineParamsForServer(
50
50
  FROM_CLIENT_FOR_SERVER_DEFAULT_KEY,
51
- );
51
+ )
52
52
 
53
53
  if (import.meta.vitest) {
54
- const { it, expect } = import.meta.vitest;
54
+ const { it, expect } = import.meta.vitest
55
55
 
56
- it("should move params to query._$client", () => {
56
+ it('should move params to query._$client', () => {
57
57
  expect(
58
58
  paramsForServer(
59
- "a",
60
- "b",
59
+ 'a',
60
+ 'b',
61
61
  )({
62
62
  params: {
63
63
  a: 1,
@@ -74,12 +74,12 @@ if (import.meta.vitest) {
74
74
  },
75
75
  },
76
76
  },
77
- });
78
- });
77
+ })
78
+ })
79
79
 
80
- it("should move params to query._$client and leave remaining", () => {
80
+ it('should move params to query._$client and leave remaining', () => {
81
81
  expect(
82
- paramsForServer("a")({
82
+ paramsForServer('a')({
83
83
  params: {
84
84
  a: 1,
85
85
  b: 2,
@@ -95,6 +95,6 @@ if (import.meta.vitest) {
95
95
  },
96
96
  },
97
97
  },
98
- });
99
- });
98
+ })
99
+ })
100
100
  }