feathers-utils 7.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 (57) hide show
  1. package/README.md +3 -4
  2. package/dist/index.cjs +28 -42
  3. package/dist/index.d.cts +27 -28
  4. package/dist/index.d.mts +27 -28
  5. package/dist/index.d.ts +27 -28
  6. package/dist/index.mjs +28 -42
  7. package/package.json +19 -22
  8. package/src/filters/array.ts +11 -13
  9. package/src/filters/index.ts +2 -2
  10. package/src/filters/object.ts +11 -11
  11. package/src/hooks/checkMulti.ts +98 -82
  12. package/src/hooks/createRelated.ts +22 -23
  13. package/src/hooks/forEach.ts +32 -32
  14. package/src/hooks/from-client-for-server/common.ts +1 -1
  15. package/src/hooks/from-client-for-server/index.ts +2 -2
  16. package/src/hooks/from-client-for-server/paramsForServer.ts +32 -32
  17. package/src/hooks/from-client-for-server/paramsFromClient.ts +25 -25
  18. package/src/hooks/index.ts +9 -9
  19. package/src/hooks/onDelete.ts +32 -32
  20. package/src/hooks/parseFields.ts +13 -13
  21. package/src/hooks/removeRelated.ts +20 -20
  22. package/src/hooks/runPerItem.ts +17 -18
  23. package/src/hooks/setData.ts +295 -264
  24. package/src/index.ts +6 -6
  25. package/src/mixins/debounce-mixin/DebouncedStore.ts +29 -29
  26. package/src/mixins/debounce-mixin/debounceMixin.ts +17 -17
  27. package/src/mixins/debounce-mixin/index.ts +3 -3
  28. package/src/mixins/debounce-mixin/types.ts +9 -9
  29. package/src/mixins/debounce-mixin/utils.ts +3 -3
  30. package/src/mixins/index.ts +1 -1
  31. package/src/types.ts +3 -5
  32. package/src/typesInternal.ts +14 -14
  33. package/src/utility-types/index.ts +48 -48
  34. package/src/utils/_utils.internal.ts +5 -5
  35. package/src/utils/defineHooks.ts +8 -8
  36. package/src/utils/deflattenQuery.ts +31 -31
  37. package/src/utils/filterQuery.ts +58 -58
  38. package/src/utils/flattenQuery.ts +54 -54
  39. package/src/utils/getItemsIsArray.ts +148 -149
  40. package/src/utils/getPaginate.ts +31 -31
  41. package/src/utils/index.ts +17 -17
  42. package/src/utils/isMulti.ts +48 -40
  43. package/src/utils/isPaginated.ts +30 -30
  44. package/src/utils/markHookForSkip.ts +177 -178
  45. package/src/utils/mergeQuery/index.ts +3 -3
  46. package/src/utils/mergeQuery/mergeArrays.ts +67 -67
  47. package/src/utils/mergeQuery/mergeQuery.ts +211 -211
  48. package/src/utils/mergeQuery/types.ts +12 -12
  49. package/src/utils/mergeQuery/utils.ts +224 -224
  50. package/src/utils/optimizeBatchPatch.ts +42 -42
  51. package/src/utils/pushSet.ts +57 -57
  52. package/src/utils/setQueryKeySafely.ts +68 -68
  53. package/src/utils/setResultEmpty.ts +125 -123
  54. package/src/utils/shouldSkip.ts +72 -72
  55. package/src/utils/toJSON.ts +4 -4
  56. package/src/utils/validateQueryProperty.ts +10 -10
  57. 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 { MaybeArray, 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
  /**
@@ -18,45 +18,44 @@ export function createRelated<
18
18
  H extends HookContext = HookContext,
19
19
  >(options: MaybeArray<CreateRelatedOptions<S>>) {
20
20
  return async (context: H) => {
21
- if (shouldSkip("createRelated", context)) {
22
- return context;
21
+ if (shouldSkip('createRelated', context)) {
22
+ return context
23
23
  }
24
24
 
25
- checkContext(context, "after", undefined, "createRelated");
25
+ checkContext(context, 'after', undefined, 'createRelated')
26
26
 
27
- const { items } = getItemsIsArray(context);
27
+ const { items } = getItemsIsArray(context)
28
28
 
29
- const entries = Array.isArray(options) ? options : [options];
29
+ const entries = Array.isArray(options) ? options : [options]
30
30
 
31
31
  await Promise.all(
32
32
  entries.map(async (entry) => {
33
- const { data, service, createItemsInDataArraySeparately, multi } =
34
- entry;
33
+ const { data, service, createItemsInDataArraySeparately, multi } = entry
35
34
 
36
35
  let dataToCreate = (
37
36
  await Promise.all(items.map(async (item) => data(item, context)))
38
- ).filter((x) => !!x);
37
+ ).filter((x) => !!x)
39
38
 
40
39
  if (createItemsInDataArraySeparately) {
41
- dataToCreate = dataToCreate.flat();
40
+ dataToCreate = dataToCreate.flat()
42
41
  }
43
42
 
44
43
  if (!dataToCreate || dataToCreate.length <= 0) {
45
- return context;
44
+ return context
46
45
  }
47
46
 
48
47
  if (multi) {
49
- await context.app.service(service as string).create(dataToCreate);
48
+ await context.app.service(service as string).create(dataToCreate)
50
49
  } else {
51
50
  await Promise.all(
52
51
  dataToCreate.map(async (item) =>
53
52
  context.app.service(service as string).create(item),
54
53
  ),
55
- );
54
+ )
56
55
  }
57
56
  }),
58
- );
57
+ )
59
58
 
60
- return context;
61
- };
59
+ return context
60
+ }
62
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
  }