feathers-utils 2.0.0-9 → 2.1.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 (156) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -1
  3. package/dist/index.cjs +964 -0
  4. package/dist/index.d.ts +262 -5
  5. package/dist/index.mjs +938 -0
  6. package/package.json +35 -38
  7. package/src/filters/object.ts +1 -1
  8. package/src/hooks/forEach.ts +47 -0
  9. package/src/hooks/index.ts +3 -0
  10. package/src/hooks/makeSequelizeQuery.ts_ +4 -4
  11. package/src/hooks/parseFields.ts +27 -0
  12. package/src/hooks/runPerItem.ts +4 -6
  13. package/src/hooks/setData.ts +4 -5
  14. package/src/index.ts +1 -0
  15. package/src/typesInternal.ts +7 -0
  16. package/src/utility-types/index.ts +116 -0
  17. package/src/utils/getItemsIsArray.ts +27 -11
  18. package/src/utils/getPaginate.ts +2 -1
  19. package/src/utils/internal.utils.ts +6 -0
  20. package/src/utils/mergeQuery/mergeQuery.ts +62 -25
  21. package/src/utils/mergeQuery/utils.ts +82 -29
  22. package/dist/esm/filters/array.d.ts +0 -2
  23. package/dist/esm/filters/array.js +0 -17
  24. package/dist/esm/filters/index.d.ts +0 -2
  25. package/dist/esm/filters/index.js +0 -2
  26. package/dist/esm/filters/object.d.ts +0 -2
  27. package/dist/esm/filters/object.js +0 -15
  28. package/dist/esm/hooks/checkMulti.d.ts +0 -5
  29. package/dist/esm/hooks/checkMulti.js +0 -20
  30. package/dist/esm/hooks/createRelated.d.ts +0 -12
  31. package/dist/esm/hooks/createRelated.js +0 -31
  32. package/dist/esm/hooks/index.d.ts +0 -6
  33. package/dist/esm/hooks/index.js +0 -6
  34. package/dist/esm/hooks/onDelete.d.ts +0 -12
  35. package/dist/esm/hooks/onDelete.js +0 -47
  36. package/dist/esm/hooks/removeRelated.d.ts +0 -11
  37. package/dist/esm/hooks/removeRelated.js +0 -37
  38. package/dist/esm/hooks/runPerItem.d.ts +0 -10
  39. package/dist/esm/hooks/runPerItem.js +0 -29
  40. package/dist/esm/hooks/setData.d.ts +0 -11
  41. package/dist/esm/hooks/setData.js +0 -46
  42. package/dist/esm/index.d.ts +0 -5
  43. package/dist/esm/index.js +0 -5
  44. package/dist/esm/mixins/debounce-mixin/DebouncedStore.d.ts +0 -18
  45. package/dist/esm/mixins/debounce-mixin/DebouncedStore.js +0 -46
  46. package/dist/esm/mixins/debounce-mixin/debounceMixin.d.ts +0 -3
  47. package/dist/esm/mixins/debounce-mixin/debounceMixin.js +0 -19
  48. package/dist/esm/mixins/debounce-mixin/index.d.ts +0 -3
  49. package/dist/esm/mixins/debounce-mixin/index.js +0 -3
  50. package/dist/esm/mixins/debounce-mixin/types.d.ts +0 -13
  51. package/dist/esm/mixins/debounce-mixin/types.js +0 -1
  52. package/dist/esm/mixins/index.d.ts +0 -1
  53. package/dist/esm/mixins/index.js +0 -1
  54. package/dist/esm/types.d.ts +0 -3
  55. package/dist/esm/types.js +0 -1
  56. package/dist/esm/typesInternal.d.ts +0 -3
  57. package/dist/esm/typesInternal.js +0 -3
  58. package/dist/esm/utils/filterQuery.d.ts +0 -8
  59. package/dist/esm/utils/filterQuery.js +0 -30
  60. package/dist/esm/utils/getItemsIsArray.d.ts +0 -10
  61. package/dist/esm/utils/getItemsIsArray.js +0 -16
  62. package/dist/esm/utils/getPaginate.d.ts +0 -9
  63. package/dist/esm/utils/getPaginate.js +0 -20
  64. package/dist/esm/utils/index.d.ts +0 -11
  65. package/dist/esm/utils/index.js +0 -11
  66. package/dist/esm/utils/isMulti.d.ts +0 -11
  67. package/dist/esm/utils/isMulti.js +0 -26
  68. package/dist/esm/utils/isPaginated.d.ts +0 -5
  69. package/dist/esm/utils/isPaginated.js +0 -11
  70. package/dist/esm/utils/markHookForSkip.d.ts +0 -7
  71. package/dist/esm/utils/markHookForSkip.js +0 -18
  72. package/dist/esm/utils/mergeQuery/index.d.ts +0 -3
  73. package/dist/esm/utils/mergeQuery/index.js +0 -3
  74. package/dist/esm/utils/mergeQuery/mergeArrays.d.ts +0 -3
  75. package/dist/esm/utils/mergeQuery/mergeArrays.js +0 -37
  76. package/dist/esm/utils/mergeQuery/mergeQuery.d.ts +0 -3
  77. package/dist/esm/utils/mergeQuery/mergeQuery.js +0 -70
  78. package/dist/esm/utils/mergeQuery/types.d.ts +0 -13
  79. package/dist/esm/utils/mergeQuery/types.js +0 -1
  80. package/dist/esm/utils/mergeQuery/utils.d.ts +0 -11
  81. package/dist/esm/utils/mergeQuery/utils.js +0 -272
  82. package/dist/esm/utils/pushSet.d.ts +0 -8
  83. package/dist/esm/utils/pushSet.js +0 -22
  84. package/dist/esm/utils/setResultEmpty.d.ts +0 -5
  85. package/dist/esm/utils/setResultEmpty.js +0 -28
  86. package/dist/esm/utils/shouldSkip.d.ts +0 -8
  87. package/dist/esm/utils/shouldSkip.js +0 -29
  88. package/dist/esm/utils/validateQueryProperty.d.ts +0 -5
  89. package/dist/esm/utils/validateQueryProperty.js +0 -23
  90. package/dist/filters/array.d.ts +0 -2
  91. package/dist/filters/array.js +0 -21
  92. package/dist/filters/index.d.ts +0 -2
  93. package/dist/filters/index.js +0 -18
  94. package/dist/filters/object.d.ts +0 -2
  95. package/dist/filters/object.js +0 -22
  96. package/dist/hooks/checkMulti.d.ts +0 -5
  97. package/dist/hooks/checkMulti.js +0 -24
  98. package/dist/hooks/createRelated.d.ts +0 -12
  99. package/dist/hooks/createRelated.js +0 -44
  100. package/dist/hooks/index.d.ts +0 -6
  101. package/dist/hooks/index.js +0 -22
  102. package/dist/hooks/onDelete.d.ts +0 -12
  103. package/dist/hooks/onDelete.js +0 -60
  104. package/dist/hooks/removeRelated.d.ts +0 -11
  105. package/dist/hooks/removeRelated.js +0 -50
  106. package/dist/hooks/runPerItem.d.ts +0 -10
  107. package/dist/hooks/runPerItem.js +0 -42
  108. package/dist/hooks/setData.d.ts +0 -11
  109. package/dist/hooks/setData.js +0 -54
  110. package/dist/index.js +0 -21
  111. package/dist/mixins/debounce-mixin/DebouncedStore.d.ts +0 -18
  112. package/dist/mixins/debounce-mixin/DebouncedStore.js +0 -65
  113. package/dist/mixins/debounce-mixin/debounceMixin.d.ts +0 -3
  114. package/dist/mixins/debounce-mixin/debounceMixin.js +0 -23
  115. package/dist/mixins/debounce-mixin/index.d.ts +0 -3
  116. package/dist/mixins/debounce-mixin/index.js +0 -19
  117. package/dist/mixins/debounce-mixin/types.d.ts +0 -13
  118. package/dist/mixins/debounce-mixin/types.js +0 -2
  119. package/dist/mixins/index.d.ts +0 -1
  120. package/dist/mixins/index.js +0 -17
  121. package/dist/types.d.ts +0 -3
  122. package/dist/types.js +0 -2
  123. package/dist/typesInternal.d.ts +0 -3
  124. package/dist/typesInternal.js +0 -4
  125. package/dist/utils/filterQuery.d.ts +0 -8
  126. package/dist/utils/filterQuery.js +0 -46
  127. package/dist/utils/getItemsIsArray.d.ts +0 -10
  128. package/dist/utils/getItemsIsArray.js +0 -20
  129. package/dist/utils/getPaginate.d.ts +0 -9
  130. package/dist/utils/getPaginate.js +0 -22
  131. package/dist/utils/index.d.ts +0 -11
  132. package/dist/utils/index.js +0 -27
  133. package/dist/utils/isMulti.d.ts +0 -11
  134. package/dist/utils/isMulti.js +0 -30
  135. package/dist/utils/isPaginated.d.ts +0 -5
  136. package/dist/utils/isPaginated.js +0 -15
  137. package/dist/utils/markHookForSkip.d.ts +0 -7
  138. package/dist/utils/markHookForSkip.js +0 -22
  139. package/dist/utils/mergeQuery/index.d.ts +0 -3
  140. package/dist/utils/mergeQuery/index.js +0 -19
  141. package/dist/utils/mergeQuery/mergeArrays.d.ts +0 -3
  142. package/dist/utils/mergeQuery/mergeArrays.js +0 -41
  143. package/dist/utils/mergeQuery/mergeQuery.d.ts +0 -3
  144. package/dist/utils/mergeQuery/mergeQuery.js +0 -77
  145. package/dist/utils/mergeQuery/types.d.ts +0 -13
  146. package/dist/utils/mergeQuery/types.js +0 -2
  147. package/dist/utils/mergeQuery/utils.d.ts +0 -11
  148. package/dist/utils/mergeQuery/utils.js +0 -287
  149. package/dist/utils/pushSet.d.ts +0 -8
  150. package/dist/utils/pushSet.js +0 -29
  151. package/dist/utils/setResultEmpty.d.ts +0 -5
  152. package/dist/utils/setResultEmpty.js +0 -32
  153. package/dist/utils/shouldSkip.d.ts +0 -8
  154. package/dist/utils/shouldSkip.js +0 -33
  155. package/dist/utils/validateQueryProperty.d.ts +0 -5
  156. package/dist/utils/validateQueryProperty.js +0 -25
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feathers-utils",
3
- "version": "2.0.0-9",
3
+ "version": "2.1.0",
4
4
  "description": "Some utils for projects using '@feathersjs/feathers'",
5
5
  "author": "fratzinger",
6
6
  "repository": {
@@ -8,23 +8,19 @@
8
8
  "url": "https://github.com/fratzinger/feathers-utils"
9
9
  },
10
10
  "engines": {
11
- "node": ">= 14"
11
+ "node": ">= 16"
12
12
  },
13
13
  "homepage": "https://github.com/fratzinger/feathers-utils",
14
14
  "license": "MIT",
15
- "main": "dist/index.js",
16
- "module": "dist/esm/index.js",
17
- "types": "dist/index.d.ts",
15
+ "type": "module",
18
16
  "exports": {
19
17
  ".": {
20
- "import": "./dist/esm/index.js",
21
- "require": "./dist/index.js",
22
- "types": "./dist/index.d.ts"
18
+ "import": "./dist/index.mjs",
19
+ "require": "./dist/index.cjs"
23
20
  }
24
21
  },
25
- "directories": {
26
- "dist": "dist"
27
- },
22
+ "main": "./dist/index.cjs",
23
+ "types": "./dist/index.d.ts",
28
24
  "files": [
29
25
  "CHANGELOG.md",
30
26
  "LICENSE",
@@ -34,44 +30,45 @@
34
30
  "dist/**"
35
31
  ],
36
32
  "scripts": {
37
- "build": "shx rm -rf dist/ && npm run tsc",
38
- "tsc": "tsc -p tsconfig.json && tsc -p tsconfig-esm.json",
33
+ "build": "unbuild",
39
34
  "version": "npm run build",
40
35
  "release": "np",
41
- "test": "cross-env NODE_ENV=test TS_NODE_PROJECT='tsconfig.test.json' mocha --require ts-node/register 'test/**/*.test.ts' --timeout 5000",
42
- "coverage": "nyc npm run test",
36
+ "test": "vitest run",
37
+ "vitest": "vitest",
38
+ "coverage": "vitest run --coverage",
43
39
  "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
44
40
  },
45
41
  "dependencies": {
46
- "@feathersjs/adapter-commons": "5.0.0-pre.33",
47
- "@feathersjs/errors": "5.0.0-pre.33",
48
- "@feathersjs/feathers": "5.0.0-pre.33",
49
- "feathers-hooks-common": "^7.0.0-pre.1",
50
- "lodash": "^4.17.21",
51
- "type-fest": "^3.2.0"
42
+ "@feathersjs/adapter-commons": "5.0.0",
43
+ "@feathersjs/commons": "^5.0.0",
44
+ "@feathersjs/errors": "5.0.0",
45
+ "@feathersjs/feathers": "5.0.0",
46
+ "fast-equals": "^5.0.1",
47
+ "feathers-hooks-common": "^7.0.0",
48
+ "lodash": "^4.17.21"
52
49
  },
53
50
  "devDependencies": {
54
- "@feathersjs/memory": "^5.0.0-pre.33",
51
+ "@feathersjs/memory": "^5.0.0",
55
52
  "@istanbuljs/nyc-config-typescript": "^1.0.2",
56
- "@types/lodash": "^4.14.189",
57
- "@types/mocha": "^10.0.0",
58
- "@types/node": "^18.11.9",
59
- "@typescript-eslint/eslint-plugin": "^5.44.0",
60
- "@typescript-eslint/parser": "^5.44.0",
53
+ "@types/lodash": "^4.14.191",
54
+ "@types/mocha": "^10.0.1",
55
+ "@types/node": "^18.14.1",
56
+ "@typescript-eslint/eslint-plugin": "^5.53.0",
57
+ "@typescript-eslint/parser": "^5.53.0",
58
+ "@vitest/coverage-c8": "^0.29.1",
61
59
  "cross-env": "^7.0.3",
62
- "eslint": "^8.28.0",
63
- "eslint-config-prettier": "^8.5.0",
64
- "eslint-import-resolver-typescript": "^3.5.2",
65
- "eslint-plugin-import": "^2.26.0",
60
+ "eslint": "^8.34.0",
61
+ "eslint-config-prettier": "^8.6.0",
62
+ "eslint-import-resolver-typescript": "^3.5.3",
63
+ "eslint-plugin-import": "^2.27.5",
66
64
  "eslint-plugin-prettier": "^4.2.1",
67
- "eslint-plugin-security": "^1.5.0",
68
- "feathers-memory": "^4.1.0",
69
- "mocha": "^10.1.0",
70
- "np": "^7.6.2",
65
+ "eslint-plugin-security": "^1.7.1",
66
+ "np": "^7.6.3",
71
67
  "nyc": "^15.1.0",
72
- "prettier": "^2.8.0",
68
+ "prettier": "^2.8.4",
73
69
  "shx": "^0.3.4",
74
- "ts-node": "^10.9.1",
75
- "typescript": "^4.9.3"
70
+ "typescript": "^4.9.5",
71
+ "unbuild": "^1.1.2",
72
+ "vitest": "^0.29.1"
76
73
  }
77
74
  }
@@ -1,6 +1,6 @@
1
1
  import type { FilterQueryOptions } from "@feathersjs/adapter-commons";
2
2
  import { validateQueryProperty } from "../utils/validateQueryProperty";
3
- import _isObject from "lodash/isObject";
3
+ import _isObject from "lodash/isObject.js";
4
4
 
5
5
  const filterQueryObject =
6
6
  (key: string) =>
@@ -0,0 +1,47 @@
1
+ import { shouldSkip } from "../utils/shouldSkip";
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";
7
+
8
+ export interface HookForEachOptions {
9
+ wait?: "sequential" | "parallel" | false
10
+ items?: GetItemsIsArrayOptions["from"]
11
+ }
12
+
13
+ export const forEach = (
14
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
+ actionPerItem: (item: any, context: HookContext) => Promisable<any>,
16
+ _options?: HookForEachOptions
17
+ ): ReturnAsyncHook => {
18
+ const options: Required<HookForEachOptions> = {
19
+ wait: "parallel",
20
+ items: "automatic",
21
+ ..._options
22
+ };
23
+
24
+ return async (context: HookContext): Promise<HookContext> => {
25
+ if (shouldSkip("runForItems", context)) { return context; }
26
+
27
+ const { items } = getItemsIsArray(context, { from: options.items });
28
+
29
+ const promises: Promise<any>[] = [];
30
+
31
+ for (const item of items) {
32
+ const promise = actionPerItem(item, context);
33
+
34
+ if (options.wait === "sequential") {
35
+ await promise;
36
+ } else {
37
+ promises.push(promise);
38
+ }
39
+ }
40
+
41
+ if (options.wait === "parallel") {
42
+ await Promise.all(promises);
43
+ }
44
+
45
+ return context;
46
+ };
47
+ };
@@ -1,6 +1,9 @@
1
1
  export * from "./checkMulti";
2
2
  export * from "./createRelated";
3
+ export * from "./forEach";
3
4
  export * from "./onDelete";
5
+ export * from "./parseFields";
4
6
  export * from "./removeRelated";
5
7
  export * from "./runPerItem";
6
8
  export * from "./setData";
9
+
@@ -1,7 +1,7 @@
1
- import _get from "lodash/get";
2
- import _isObject from "lodash/isObject";
3
- import _transform from "lodash/transform";
4
- import _intersection from "lodash/intersection";
1
+ import _get from "lodash/get.js";
2
+ import _isObject from "lodash/isObject.js";
3
+ import _transform from "lodash/transform.js";
4
+ import _intersection from "lodash/intersection.js";
5
5
  import { HookContext, Query } from "@feathersjs/feathers";
6
6
 
7
7
  import { Model } from "sequelize";
@@ -0,0 +1,27 @@
1
+ import type { HookContext } from "@feathersjs/feathers";
2
+ import { getItemsIsArray } from "../utils/getItemsIsArray";
3
+
4
+ /**
5
+ * Parse fields to date or number
6
+ * skips undefined fields
7
+ */
8
+ export const parseFields = (type: "date" | "number", options: { fields: string[] }) => (context: HookContext) => {
9
+ const { items } = getItemsIsArray(context);
10
+
11
+ items.forEach(item => {
12
+ options.fields.forEach(field => {
13
+ // ignore undefined fields
14
+ if (!(field in item)) {
15
+ return;
16
+ }
17
+
18
+ if (type === "date") {
19
+ item[field] = new Date(item[field]);
20
+ } else if (type === "number") {
21
+ item[field] = Number(item[field]);
22
+ }
23
+ });
24
+ });
25
+
26
+ return context;
27
+ };
@@ -11,12 +11,10 @@ const makeOptions = (
11
11
  options?: HookRunPerItemOptions
12
12
  ): Required<HookRunPerItemOptions> => {
13
13
  options = options || {};
14
- return Object.assign(
15
- {
16
- wait: true,
17
- },
18
- options
19
- );
14
+ return {
15
+ wait: true,
16
+ ...options,
17
+ };
20
18
  };
21
19
 
22
20
  /**
@@ -27,11 +27,10 @@ export function setData<H extends HookContext = HookContext>(
27
27
  to: PropertyPath,
28
28
  _options?: HookSetDataOptions
29
29
  ) {
30
- const options: Required<HookSetDataOptions> = Object.assign(
31
- {},
32
- defaultOptions,
33
- _options
34
- );
30
+ const options: Required<HookSetDataOptions> = {
31
+ ...defaultOptions,
32
+ ..._options,
33
+ };
35
34
  return (context: H) => {
36
35
  if (shouldSkip("setData", context)) {
37
36
  return context;
package/src/index.ts CHANGED
@@ -5,3 +5,4 @@ export * from "./utils";
5
5
  export * from "./filters";
6
6
 
7
7
  export * from "./types";
8
+ export * from "./utility-types";
@@ -1,6 +1,13 @@
1
1
  // here are types that are not meant to be exported!
2
2
  // just for internal use of this package
3
3
 
4
+ import { HookContext } from "@feathersjs/feathers/lib";
5
+
4
6
  export type MaybeArray<T> = T | T[];
5
7
  export type Promisable<T> = T | Promise<T>;
6
8
  export type Path = Array<string | number>;
9
+
10
+ export type HookType = "before" | "after" | "error";
11
+ export type ServiceMethodName = "find" | "get" | "create" | "update" | "patch" | "remove";
12
+ export type ReturnSyncHook = (context: HookContext) => HookContext
13
+ export type ReturnAsyncHook = (context: HookContext) => Promise<HookContext>
@@ -0,0 +1,116 @@
1
+ import type { Application, Id } from "@feathersjs/feathers";
2
+
3
+ type Single<T> = T extends Array<infer U> ? U : T;
4
+ type AsArray<T> = T extends any[] ? T : [T];
5
+
6
+ export type InferCreateData<S> = S extends {
7
+ create: (data: infer D, params: any) => any;
8
+ }
9
+ ? D
10
+ : never;
11
+
12
+ export type InferCreateDataSingle<S> = Single<InferCreateData<S>>;
13
+
14
+ export type InferUpdateData<S> = S extends {
15
+ update: (id: any, data: infer D, params: any) => any;
16
+ }
17
+ ? D
18
+ : never;
19
+
20
+ export type InferPatchData<S> = S extends {
21
+ patch: (id: any, data: infer D, params: any) => any;
22
+ }
23
+ ? D
24
+ : never;
25
+
26
+ export type InferGetResult<S> = S extends {
27
+ get: (id: any, params: any) => infer R;
28
+ }
29
+ ? Awaited<R>
30
+ : never;
31
+
32
+ export type InferFindResult<S> = S extends {
33
+ find: (params: any) => infer R;
34
+ }
35
+ ? Awaited<R>
36
+ : never;
37
+
38
+ export type InferCreateResult<S, D = unknown> = S extends {
39
+ create: (data: any, params: any) => infer R;
40
+ }
41
+ ? D extends any[]
42
+ ? AsArray<Awaited<R>>
43
+ : D extends InferCreateDataSingle<S>
44
+ ? Single<Awaited<R>>
45
+ : Awaited<R>
46
+ : never;
47
+
48
+ export type InferCreateResultSingle<S> = Single<InferCreateResult<S>>;
49
+
50
+ export type InferUpdateResult<S> = S extends {
51
+ update: (id: any, data: any, params: any) => infer R;
52
+ }
53
+ ? Awaited<R>
54
+ : never;
55
+
56
+ export type InferPatchResult<S, IdOrNullable = any> = S extends {
57
+ patch: (id: Id, data: any, params: any) => infer R;
58
+ }
59
+ ? IdOrNullable extends Id
60
+ ? Single<Awaited<R>>
61
+ : IdOrNullable extends null
62
+ ? AsArray<Awaited<R>>
63
+ : Awaited<R>
64
+ : never;
65
+
66
+ export type InferRemoveResult<S, IdOrNullable = any> = S extends {
67
+ remove: (id: IdOrNullable, params: any) => infer R;
68
+ }
69
+ ? IdOrNullable extends Id
70
+ ? Single<Awaited<R>>
71
+ : IdOrNullable extends null
72
+ ? AsArray<Awaited<R>>
73
+ : Awaited<R>
74
+ : never;
75
+
76
+ export type GetService<App extends Application, Path extends string> = App["services"][Path];
77
+
78
+ export type InferGetResultFromPath<App extends Application, Path extends string> = InferGetResult<GetService<App, Path>>;
79
+ export type InferFindResultFromPath<App extends Application, Path extends string> = InferFindResult<GetService<App, Path>>;
80
+
81
+ export type InferCreateDataFromPath<App extends Application, Path extends string> = InferCreateData<GetService<App, Path>>
82
+ export type InferCreateDataSingleFromPath<App extends Application, Path extends string> = InferCreateDataSingle<GetService<App, Path>>;
83
+
84
+ export type InferCreateResultFromPath<App extends Application, Path extends string, D = unknown> = InferCreateResult<GetService<App, Path>, D>;
85
+ export type InferCreateResultSingleFromPath<App extends Application, Path extends string> = InferCreateResultSingle<GetService<App, Path>>;
86
+
87
+ export type InferUpdateDataFromPath<App extends Application, Path extends string> = InferUpdateData<GetService<App, Path>>
88
+ export type InferPatchDataFromPath<App extends Application, Path extends string> = InferPatchData<GetService<App, Path>>
89
+
90
+ export type InferUpdateResultFromPath<App extends Application, Path extends string> = InferUpdateResult<GetService<App, Path>>;
91
+ export type InferPatchResultFromPath<App extends Application, Path extends string, IdOrNullable = any> = InferPatchResult<GetService<App, Path>, IdOrNullable>;
92
+
93
+ export type InferRemoveResultFromPath<App extends Application, Path extends string, IdOrNullable = any> = InferRemoveResult<GetService<App, Path>, IdOrNullable>;
94
+
95
+ export type InferDataFromPath<App extends Application, Path extends string, Method extends "create" | "update" | "patch"> = Method extends "create"
96
+ ? InferCreateDataFromPath<App, Path>
97
+ : Method extends "update"
98
+ ? InferUpdateDataFromPath<App, Path>
99
+ : Method extends "patch"
100
+ ? InferPatchDataFromPath<App, Path>
101
+ : never;
102
+
103
+ export type InferResultFromPath<App extends Application, Path extends string, Method extends "get" | "find" | "create" | "update" | "patch" | "remove"> = Method extends "get"
104
+ ? InferGetResultFromPath<App, Path>
105
+ : Method extends "find"
106
+ ? InferFindResultFromPath<App, Path>
107
+ : Method extends "create"
108
+ ? InferCreateResultFromPath<App, Path>
109
+ : Method extends "update"
110
+ ? InferUpdateResultFromPath<App, Path>
111
+ : Method extends "patch"
112
+ ? InferPatchResultFromPath<App, Path>
113
+ : Method extends "remove"
114
+ ? InferRemoveResultFromPath<App, Path>
115
+ : never;
116
+
@@ -1,22 +1,38 @@
1
1
  import type { HookContext } from "@feathersjs/feathers";
2
2
 
3
+ export type GetItemsIsArrayOptions = {
4
+ from: "data" | "result" | "automatic"
5
+ }
6
+
3
7
  export interface GetItemsIsArrayResult<T = any> {
4
- items: T[];
5
- isArray: boolean;
8
+ items: T[]
9
+ isArray: boolean
6
10
  }
7
11
 
8
- /**
9
- * util to get items from context, return it as an array, no matter if it is an array or not
10
- * uses `context.result` if existent. uses `context.data` otherwise
11
- */
12
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
12
13
  export const getItemsIsArray = <T = any, H extends HookContext = HookContext>(
13
- context: H
14
+ context: H,
15
+ options?: GetItemsIsArrayOptions
14
16
  ): GetItemsIsArrayResult<T> => {
15
- let itemOrItems = context.result !== undefined ? context.result : context.data;
16
- itemOrItems =
17
- itemOrItems && context.method === "find"
18
- ? itemOrItems.data || itemOrItems
17
+ const {
18
+ from = "automatic"
19
+ } = options || {};
20
+
21
+ let itemOrItems;
22
+
23
+ if (from === "automatic") {
24
+ itemOrItems = context.type === "before"
25
+ ? context.data
26
+ : context.result;
27
+ itemOrItems = itemOrItems && context.method === "find"
28
+ ? (itemOrItems.data || itemOrItems)
19
29
  : itemOrItems;
30
+ } else if (from === "data") {
31
+ itemOrItems = context.data;
32
+ } else if (from === "result") {
33
+ itemOrItems = context.result;
34
+ }
35
+
20
36
  const isArray = Array.isArray(itemOrItems);
21
37
  return {
22
38
  items: isArray ? itemOrItems : itemOrItems != null ? [itemOrItems] : [],
@@ -1,5 +1,6 @@
1
1
  import type { PaginationOptions } from "@feathersjs/adapter-commons";
2
2
  import type { HookContext } from "@feathersjs/feathers";
3
+ import { hasOwnProperty } from "./internal.utils";
3
4
 
4
5
  /**
5
6
  * util to get paginate options from context
@@ -10,7 +11,7 @@ import type { HookContext } from "@feathersjs/feathers";
10
11
  export const getPaginate = <H extends HookContext = HookContext>(
11
12
  context: H
12
13
  ): PaginationOptions | undefined => {
13
- if (Object.prototype.hasOwnProperty.call(context.params, "paginate")) {
14
+ if (hasOwnProperty(context.params, "paginate")) {
14
15
  return (context.params.paginate as PaginationOptions) || undefined;
15
16
  }
16
17
 
@@ -0,0 +1,6 @@
1
+ export const hasOwnProperty = (
2
+ obj: Record<string, unknown>,
3
+ ...keys: string[]
4
+ ): boolean => {
5
+ return keys.some((x) => Object.prototype.hasOwnProperty.call(obj, x));
6
+ };
@@ -2,30 +2,39 @@ import _merge from "lodash/merge.js";
2
2
  import _isEmpty from "lodash/isEmpty.js";
3
3
  import type { Query } from "@feathersjs/feathers";
4
4
  import {
5
+ areQueriesOverlapping,
5
6
  handleArray,
6
7
  handleCircular,
8
+ isQueryMoreExplicitThanQuery,
7
9
  makeDefaultOptions,
8
10
  moveProperty,
9
11
  } from "./utils";
10
12
  import type { MergeQueryOptions } from "./types";
11
13
  import { filterQuery } from "../filterQuery";
12
-
13
- export function mergeQuery<T>(
14
+ import { hasOwnProperty } from "../internal.utils";
15
+
16
+ /**
17
+ * Merges two queries into one.
18
+ * @param target Query to be merged into
19
+ * @param source Query to be merged from
20
+ * @param _options
21
+ * @returns Query
22
+ */
23
+ export function mergeQuery<T = any>(
14
24
  target: Query,
15
25
  source: Query,
16
- options?: Partial<MergeQueryOptions<T>>
26
+ _options?: Partial<MergeQueryOptions<T>>
17
27
  ): Query {
18
- const fullOptions = makeDefaultOptions(options);
28
+ const options = makeDefaultOptions(_options);
19
29
  const { filters: targetFilters, query: targetQuery } = filterQuery(target, {
20
- operators: fullOptions.operators,
21
- filters: fullOptions.filters,
22
- service: fullOptions.service,
30
+ operators: options.operators,
31
+ filters: options.filters,
32
+ service: options.service,
23
33
  });
24
34
 
25
- moveProperty(targetFilters, targetQuery, "$or");
26
- moveProperty(targetFilters, targetQuery, "$and");
35
+ moveProperty(targetFilters, targetQuery, "$or", "$and");
27
36
 
28
- if (target.$limit) {
37
+ if ("$limit" in target) {
29
38
  targetFilters.$limit = target.$limit;
30
39
  }
31
40
 
@@ -34,13 +43,12 @@ export function mergeQuery<T>(
34
43
  filters: sourceFilters,
35
44
  query: sourceQuery,
36
45
  } = filterQuery(source, {
37
- operators: fullOptions.operators,
38
- filters: fullOptions.filters,
39
- service: fullOptions.service,
46
+ operators: options.operators,
47
+ filters: options.filters,
48
+ service: options.service,
40
49
  });
41
50
 
42
- moveProperty(sourceFilters, sourceQuery, "$or");
43
- moveProperty(sourceFilters, sourceQuery, "$and");
51
+ moveProperty(sourceFilters, sourceQuery, "$or", "$and");
44
52
 
45
53
  if (source.$limit) {
46
54
  sourceFilters.$limit = source.$limit;
@@ -50,37 +58,64 @@ export function mergeQuery<T>(
50
58
 
51
59
  if (
52
60
  target &&
53
- !Object.prototype.hasOwnProperty.call(target, "$limit") &&
54
- Object.prototype.hasOwnProperty.call(targetFilters, "$limit")
61
+ !hasOwnProperty(target, "$limit") &&
62
+ hasOwnProperty(targetFilters, "$limit")
55
63
  ) {
56
64
  delete targetFilters.$limit;
57
65
  }
58
66
 
59
67
  if (
60
68
  source &&
61
- !Object.prototype.hasOwnProperty.call(source, "$limit") &&
62
- Object.prototype.hasOwnProperty.call(sourceFilters, "$limit")
69
+ !hasOwnProperty(source, "$limit") &&
70
+ hasOwnProperty(sourceFilters, "$limit")
63
71
  ) {
64
72
  delete sourceFilters.$limit;
65
73
  }
66
74
 
67
- handleArray(targetFilters, sourceFilters, ["$select"], fullOptions);
75
+ handleArray(targetFilters, sourceFilters, ["$select"], options);
68
76
  // remaining filters
69
77
  delete sourceFilters["$select"];
70
78
  _merge(targetFilters, sourceFilters);
71
79
 
72
80
  //#endregion
73
81
 
82
+ const subsetQuery = isQueryMoreExplicitThanQuery(targetQuery, sourceQuery);
83
+
84
+ if (
85
+ options.defaultHandle === "intersect" &&
86
+ !!subsetQuery &&
87
+ !hasOwnProperty(targetQuery, "$or", "$and") &&
88
+ !hasOwnProperty(sourceQuery, "$or", "$and")
89
+ ) {
90
+ return {
91
+ ...targetFilters,
92
+ ...subsetQuery,
93
+ };
94
+ }
95
+
96
+ if (
97
+ options.defaultHandle === "intersect" &&
98
+ !areQueriesOverlapping(targetQuery, sourceQuery) &&
99
+ !hasOwnProperty(targetQuery, "$or", "$and") &&
100
+ !hasOwnProperty(sourceQuery, "$or", "$and")
101
+ ) {
102
+ return {
103
+ ...targetFilters,
104
+ ...targetQuery,
105
+ ...sourceQuery,
106
+ };
107
+ }
108
+
74
109
  //#region '$or' / '$and'
75
110
 
76
111
  if (
77
- options?.useLogicalConjunction &&
112
+ options.useLogicalConjunction &&
78
113
  (options.defaultHandle === "combine" ||
79
114
  options.defaultHandle === "intersect") &&
80
115
  !_isEmpty(targetQuery)
81
116
  ) {
82
117
  const logicalOp = options.defaultHandle === "combine" ? "$or" : "$and";
83
- if (Object.prototype.hasOwnProperty.call(sourceQuery, logicalOp)) {
118
+ if (hasOwnProperty(sourceQuery, logicalOp)) {
84
119
  // omit '$or'/'$and' and put all other props into '$or'/'$and'
85
120
  const andOr = sourceQuery[logicalOp] as unknown[];
86
121
  delete sourceQuery[logicalOp];
@@ -96,9 +131,11 @@ export function mergeQuery<T>(
96
131
  const keys = Object.keys(sourceQuery);
97
132
  for (let i = 0, n = keys.length; i < n; i++) {
98
133
  const key = keys[i];
99
- handleCircular(targetQuery, sourceQuery, [key], fullOptions);
134
+ handleCircular(targetQuery, sourceQuery, [key], options);
100
135
  }
101
- const result = Object.assign({}, targetFilters, targetQuery) as Query;
102
136
 
103
- return result;
137
+ return {
138
+ ...targetFilters,
139
+ ...targetQuery,
140
+ };
104
141
  }