feathers-utils 10.3.0 → 10.4.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 (41) hide show
  1. package/dist/{hooks-DpFQfcFa.mjs → hooks-D_u2QFhM.mjs} +6 -6
  2. package/dist/{hooks-DpFQfcFa.mjs.map → hooks-D_u2QFhM.mjs.map} +1 -1
  3. package/dist/hooks.mjs +4 -4
  4. package/dist/{index-C6MN6wag.d.mts → index-H1zXVhff.d.mts} +49 -2
  5. package/dist/index.d.mts +2 -2
  6. package/dist/index.mjs +7 -7
  7. package/dist/internal.utils-CWuBYzpQ.mjs +120 -0
  8. package/dist/internal.utils-CWuBYzpQ.mjs.map +1 -0
  9. package/dist/{mutate-result.util-C0nY6L7i.mjs → mutate-result.util-mxvMl6bw.mjs} +2 -2
  10. package/dist/mutate-result.util-mxvMl6bw.mjs.map +1 -0
  11. package/dist/{predicates-NOnUyMic.mjs → predicates-CR4O2nSr.mjs} +2 -2
  12. package/dist/{predicates-NOnUyMic.mjs.map → predicates-CR4O2nSr.mjs.map} +1 -1
  13. package/dist/predicates.d.mts +1 -1
  14. package/dist/predicates.mjs +1 -1
  15. package/dist/{resolve-BflgIVD8.mjs → resolve-BbbdWdlO.mjs} +2 -2
  16. package/dist/{resolve-BflgIVD8.mjs.map → resolve-BbbdWdlO.mjs.map} +1 -1
  17. package/dist/resolvers.mjs +1 -1
  18. package/dist/{transform-result.hook-V2QYN2K0.mjs → transform-result.hook-0D676rcY.mjs} +2 -2
  19. package/dist/{transform-result.hook-V2QYN2K0.mjs.map → transform-result.hook-0D676rcY.mjs.map} +1 -1
  20. package/dist/transformers.mjs +3 -3
  21. package/dist/{utils-DByCpAsf.mjs → utils-sTvj8-Jy.mjs} +103 -22
  22. package/dist/utils-sTvj8-Jy.mjs.map +1 -0
  23. package/dist/utils.d.mts +2 -2
  24. package/dist/utils.mjs +4 -4
  25. package/package.json +1 -1
  26. package/src/{utils/merge-query → common}/dedupe-branches.ts +3 -3
  27. package/src/common/flatten-and-branches.ts +56 -0
  28. package/src/common/flatten-or-branches.ts +52 -0
  29. package/src/common/index.ts +3 -0
  30. package/src/utils/index.ts +1 -0
  31. package/src/utils/merge-query/merge-query-bodies.ts +19 -3
  32. package/src/utils/merge-query/merge-query.util.ts +5 -2
  33. package/src/utils/replace-data/replace-data.util.ts +4 -4
  34. package/src/utils/replace-result/replace-result.util.ts +18 -18
  35. package/src/utils/simplify-query/merge-and-branches-up.ts +86 -0
  36. package/src/utils/simplify-query/merge-or-branch-up.ts +74 -0
  37. package/src/utils/simplify-query/simplify-query.util.ts +98 -0
  38. package/dist/internal.utils-BBB-b6Ud.mjs +0 -69
  39. package/dist/internal.utils-BBB-b6Ud.mjs.map +0 -1
  40. package/dist/mutate-result.util-C0nY6L7i.mjs.map +0 -1
  41. package/dist/utils-DByCpAsf.mjs.map +0 -1
@@ -1,8 +1,8 @@
1
- import type { HookContext } from "@feathersjs/feathers";
2
- import { copy } from "fast-copy";
3
- import { getResultIsArray } from "../get-result-is-array/get-result-is-array.util.js";
4
- import type { DispatchOption } from "../../types.js";
5
- import type { ResultSingleHookContext } from "../../utility-types/hook-context.js";
1
+ import type { HookContext } from '@feathersjs/feathers'
2
+ import { copy } from 'fast-copy'
3
+ import { getResultIsArray } from '../get-result-is-array/get-result-is-array.util.js'
4
+ import type { DispatchOption } from '../../types.js'
5
+ import type { ResultSingleHookContext } from '../../utility-types/hook-context.js'
6
6
 
7
7
  export type ReplaceResultOptions = {
8
8
  /**
@@ -10,8 +10,8 @@ export type ReplaceResultOptions = {
10
10
  * writes both `result` and `dispatch`. When dispatch is requested and not yet
11
11
  * present, it is seeded from a clone of `context.result`.
12
12
  */
13
- dispatch?: DispatchOption;
14
- };
13
+ dispatch?: DispatchOption
14
+ }
15
15
 
16
16
  /**
17
17
  * Replaces `context.result` (and/or `context.dispatch`) wholesale with the given
@@ -34,27 +34,27 @@ export function replaceResult<H extends HookContext = HookContext>(
34
34
  options?: ReplaceResultOptions,
35
35
  ): H {
36
36
  if (!!options?.dispatch && !context.dispatch) {
37
- context.dispatch = copy(context.result);
37
+ context.dispatch = copy(context.result)
38
38
  }
39
39
 
40
40
  const write = (dispatch: boolean) => {
41
- const { isArray, key } = getResultIsArray(context, { dispatch });
41
+ const { isArray, key } = getResultIsArray(context, { dispatch })
42
42
 
43
43
  if (!isArray) {
44
- context[key] = result[0];
44
+ context[key] = result[0]
45
45
  } else if (!Array.isArray(context[key]) && context[key]?.data) {
46
- context[key].data = result;
46
+ context[key].data = result
47
47
  } else {
48
- context[key] = result;
48
+ context[key] = result
49
49
  }
50
- };
50
+ }
51
51
 
52
- if (options?.dispatch === "both") {
53
- write(true);
54
- write(false);
52
+ if (options?.dispatch === 'both') {
53
+ write(true)
54
+ write(false)
55
55
  } else {
56
- write(!!options?.dispatch);
56
+ write(!!options?.dispatch)
57
57
  }
58
58
 
59
- return context;
59
+ return context
60
60
  }
@@ -0,0 +1,86 @@
1
+ import { dequal as deepEqual } from 'dequal'
2
+
3
+ type QueryRecord = Record<string, any>
4
+
5
+ /**
6
+ * `$and` is an implicit AND with the rest of the query, so ALL of its branches can
7
+ * be merged up into the parent at once — as long as no key would be set to two
8
+ * different values (across the branches or the existing keys). On any such collision
9
+ * the `$and` is kept intact. Internal helper for {@link simplifyQuery}.
10
+ */
11
+ export function mergeAndBranchesUp(
12
+ result: QueryRecord,
13
+ enabled: boolean,
14
+ ): QueryRecord {
15
+ if (!enabled || !Array.isArray(result.$and) || result.$and.length === 0) {
16
+ return result
17
+ }
18
+ const rest = { ...result }
19
+ delete rest.$and
20
+ const seen = new Map<string, any>(Object.entries(rest))
21
+ for (const branch of result.$and) {
22
+ for (const [key, value] of Object.entries(branch)) {
23
+ if (seen.has(key) && !deepEqual(seen.get(key), value)) {
24
+ return result
25
+ }
26
+ seen.set(key, value)
27
+ }
28
+ }
29
+ return Object.assign(rest, ...result.$and)
30
+ }
31
+
32
+ if (import.meta.vitest) {
33
+ const { describe, it, expect } = import.meta.vitest
34
+
35
+ describe('mergeAndBranchesUp', () => {
36
+ it('merges all branches up when keys do not collide', () => {
37
+ expect(mergeAndBranchesUp({ $and: [{ a: 1 }, { b: 2 }] }, true)).toEqual({
38
+ a: 1,
39
+ b: 2,
40
+ })
41
+ })
42
+
43
+ it('merges branches up alongside existing root keys', () => {
44
+ expect(
45
+ mergeAndBranchesUp({ c: 3, $and: [{ a: 1 }, { b: 2 }] }, true),
46
+ ).toEqual({ a: 1, b: 2, c: 3 })
47
+ })
48
+
49
+ it('keeps the $and on a value collision between branches', () => {
50
+ expect(mergeAndBranchesUp({ $and: [{ a: 1 }, { a: 2 }] }, true)).toEqual({
51
+ $and: [{ a: 1 }, { a: 2 }],
52
+ })
53
+ })
54
+
55
+ it('keeps the $and on a collision with a root key', () => {
56
+ expect(mergeAndBranchesUp({ a: 1, $and: [{ a: 2 }] }, true)).toEqual({
57
+ a: 1,
58
+ $and: [{ a: 2 }],
59
+ })
60
+ })
61
+
62
+ it('allows a branch key equal to an existing root value', () => {
63
+ expect(
64
+ mergeAndBranchesUp({ a: 1, $and: [{ a: 1 }, { b: 2 }] }, true),
65
+ ).toEqual({ a: 1, b: 2 })
66
+ })
67
+
68
+ it('does nothing when disabled', () => {
69
+ expect(mergeAndBranchesUp({ $and: [{ a: 1 }] }, false)).toEqual({
70
+ $and: [{ a: 1 }],
71
+ })
72
+ })
73
+
74
+ it('ignores a missing or empty $and', () => {
75
+ expect(mergeAndBranchesUp({ a: 1 }, true)).toEqual({ a: 1 })
76
+ expect(mergeAndBranchesUp({ $and: [] }, true)).toEqual({ $and: [] })
77
+ })
78
+
79
+ it('does not mutate the input', () => {
80
+ const query = { c: 3, $and: [{ a: 1 }, { b: 2 }] }
81
+ const snapshot = structuredClone(query)
82
+ mergeAndBranchesUp(query, true)
83
+ expect(query).toEqual(snapshot)
84
+ })
85
+ })
86
+ }
@@ -0,0 +1,74 @@
1
+ import { dequal as deepEqual } from 'dequal'
2
+
3
+ type QueryRecord = Record<string, any>
4
+
5
+ /**
6
+ * `$or` is a disjunction, so only a *single* branch can be merged up (an `$or` of
7
+ * one is just that branch) — and only when no key would collide. This is the key
8
+ * asymmetry with `$and` ({@link mergeAndBranchesUp}). Internal helper for
9
+ * {@link simplifyQuery}.
10
+ */
11
+ export function mergeOrBranchUp(
12
+ result: QueryRecord,
13
+ enabled: boolean,
14
+ ): QueryRecord {
15
+ if (!enabled || !Array.isArray(result.$or) || result.$or.length !== 1) {
16
+ return result
17
+ }
18
+ const branch = result.$or[0]
19
+ const rest = { ...result }
20
+ delete rest.$or
21
+ for (const key of Object.keys(branch)) {
22
+ if (key in rest && !deepEqual(rest[key], branch[key])) {
23
+ return result
24
+ }
25
+ }
26
+ return { ...rest, ...branch }
27
+ }
28
+
29
+ if (import.meta.vitest) {
30
+ const { describe, it, expect } = import.meta.vitest
31
+
32
+ describe('mergeOrBranchUp', () => {
33
+ it('merges a single branch up when keys do not collide', () => {
34
+ expect(mergeOrBranchUp({ a: 1, $or: [{ b: 2 }] }, true)).toEqual({
35
+ a: 1,
36
+ b: 2,
37
+ })
38
+ })
39
+
40
+ it('collapses a sole single-branch $or to that branch', () => {
41
+ expect(mergeOrBranchUp({ $or: [{ a: 1 }] }, true)).toEqual({ a: 1 })
42
+ })
43
+
44
+ it('keeps the $or on a collision with a root key', () => {
45
+ expect(mergeOrBranchUp({ a: 1, $or: [{ a: 2 }] }, true)).toEqual({
46
+ a: 1,
47
+ $or: [{ a: 2 }],
48
+ })
49
+ })
50
+
51
+ it('does NOT merge a multi-branch $or', () => {
52
+ expect(mergeOrBranchUp({ $or: [{ a: 1 }, { b: 2 }] }, true)).toEqual({
53
+ $or: [{ a: 1 }, { b: 2 }],
54
+ })
55
+ })
56
+
57
+ it('does nothing when disabled', () => {
58
+ expect(mergeOrBranchUp({ $or: [{ a: 1 }] }, false)).toEqual({
59
+ $or: [{ a: 1 }],
60
+ })
61
+ })
62
+
63
+ it('ignores a missing $or', () => {
64
+ expect(mergeOrBranchUp({ a: 1 }, true)).toEqual({ a: 1 })
65
+ })
66
+
67
+ it('does not mutate the input', () => {
68
+ const query = { a: 1, $or: [{ b: 2 }] }
69
+ const snapshot = structuredClone(query)
70
+ mergeOrBranchUp(query, true)
71
+ expect(query).toEqual(snapshot)
72
+ })
73
+ })
74
+ }
@@ -0,0 +1,98 @@
1
+ import type { Query } from '@feathersjs/feathers'
2
+ import { isEmptyObject } from '../../common/is-empty-object.js'
3
+ import { dedupeBranches } from '../../common/dedupe-branches.js'
4
+ import { flattenAndBranches } from '../../common/flatten-and-branches.js'
5
+ import { flattenOrBranches } from '../../common/flatten-or-branches.js'
6
+ import { mergeAndBranchesUp } from './merge-and-branches-up.js'
7
+ import { mergeOrBranchUp } from './merge-or-branch-up.js'
8
+
9
+ export interface SimplifyQueryOptions {
10
+ /**
11
+ * Dissolve a top-level single-branch `$and` by merging its branch up into the
12
+ * query (only when no key would collide). Nested levels are always dissolved.
13
+ * Default `true`.
14
+ */
15
+ replaceAnd?: boolean
16
+ /**
17
+ * Dissolve a top-level single-branch `$or` by merging its branch up into the
18
+ * query (only when no key would collide). Nested levels are always dissolved.
19
+ * Default `true`.
20
+ */
21
+ replaceOr?: boolean
22
+ }
23
+
24
+ /**
25
+ * Normalizes the logical structure of a Feathers query without changing what it
26
+ * matches: empty `$and`/`$or` are dropped, duplicate branches removed, nested
27
+ * same-operator branches hoisted (`$and`-in-`$and`, pure `$or`-in-`$or`), and
28
+ * branches merged up into the parent where it is safe — all of an `$and` when no
29
+ * key collides, a single-branch `$or`. Runs recursively. Inputs are not mutated;
30
+ * a query with nothing to simplify is returned unchanged.
31
+ *
32
+ * @param query the query to simplify (a falsy query is returned as-is)
33
+ * @param options
34
+ * @returns the simplified query
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * import { simplifyQuery } from 'feathers-utils/utils'
39
+ *
40
+ * // non-colliding $and branches (here also a hoisted nested $and) merge up
41
+ * simplifyQuery({ $and: [{ id: 1 }, { $and: [{ status: 'a' }] }] })
42
+ * // => { id: 1, status: 'a' }
43
+ *
44
+ * simplifyQuery({ $or: [{ id: 1 }] })
45
+ * // => { id: 1 }
46
+ *
47
+ * // a colliding key keeps the $and intact
48
+ * simplifyQuery({ $and: [{ price: { $gt: 1 } }, { price: { $lt: 9 } }] })
49
+ * // => { $and: [{ price: { $gt: 1 } }, { price: { $lt: 9 } }] }
50
+ * ```
51
+ *
52
+ * @see https://utils.feathersjs.com/utils/simplify-query.html
53
+ */
54
+ export function simplifyQuery<Q extends Query | null | undefined>(
55
+ query: Q,
56
+ options: SimplifyQueryOptions = {},
57
+ ): Q {
58
+ const { replaceAnd = true, replaceOr = true } = options
59
+ return simplify(query, replaceAnd, replaceOr)
60
+ }
61
+
62
+ function simplify(query: any, replaceAnd: boolean, replaceOr: boolean): any {
63
+ if (!query || typeof query !== 'object' || Array.isArray(query)) {
64
+ return query
65
+ }
66
+
67
+ const hasAnd = Array.isArray(query.$and)
68
+ const hasOr = Array.isArray(query.$or)
69
+ if (!hasAnd && !hasOr) {
70
+ return query
71
+ }
72
+
73
+ const { $and, $or, ...rest } = query
74
+ const result: Record<string, any> = { ...rest }
75
+
76
+ if (hasAnd) {
77
+ // an empty `{}` branch is the AND identity → dropped by dedupeBranches
78
+ const branches = dedupeBranches(
79
+ flattenAndBranches($and.map((b: any) => simplify(b, true, true))),
80
+ )
81
+ if (branches.length > 0) {
82
+ result.$and = branches
83
+ }
84
+ }
85
+
86
+ if (hasOr) {
87
+ const simplified = $or.map((b: any) => simplify(b, true, true))
88
+ // an empty `{}` branch makes the whole `$or` match-all → drop the `$or`
89
+ if (!simplified.some(isEmptyObject)) {
90
+ const branches = dedupeBranches(flattenOrBranches(simplified))
91
+ if (branches.length > 0) {
92
+ result.$or = branches
93
+ }
94
+ }
95
+ }
96
+
97
+ return mergeOrBranchUp(mergeAndBranchesUp(result, replaceAnd), replaceOr)
98
+ }
@@ -1,69 +0,0 @@
1
- import _traverse from "neotraverse";
2
- //#region src/common/traverse.ts
3
- /**
4
- * Recursively traverses items (or an array of items) and applies a converter function
5
- * to every node using `neotraverse`. Modifications happen in place.
6
- *
7
- * @example
8
- * ```ts
9
- * traverse(items, function () { if (this.key === 'secret') this.remove() })
10
- * ```
11
- */
12
- function traverse(items, converter) {
13
- (Array.isArray(items) ? items : [items]).forEach((item) => {
14
- _traverse(item).forEach(converter);
15
- });
16
- }
17
- //#endregion
18
- //#region src/common/is-empty-object.ts
19
- /**
20
- * Returns `true` only for a plain empty object (`{}`). Arrays, `null`, primitives
21
- * and non-empty objects are all `false`.
22
- *
23
- * @example
24
- * ```ts
25
- * isEmptyObject({}) // => true
26
- * isEmptyObject({ a: 1 }) // => false
27
- * isEmptyObject([]) // => false
28
- * ```
29
- */
30
- const isEmptyObject = (obj) => !!obj && typeof obj === "object" && !Array.isArray(obj) && Object.keys(obj).length === 0;
31
- //#endregion
32
- //#region src/common/index.ts
33
- /**
34
- * Type guard that checks if a value is a `Promise` instance.
35
- *
36
- * @example
37
- * ```ts
38
- * const result = maybeSyncFn()
39
- * if (isPromise(result)) {
40
- * await result
41
- * }
42
- * ```
43
- */
44
- function isPromise(p) {
45
- return p instanceof Promise;
46
- }
47
- //#endregion
48
- //#region src/internal.utils.ts
49
- const hasOwnProperty = (obj, ...keys) => {
50
- return keys.some((x) => Object.prototype.hasOwnProperty.call(obj, x));
51
- };
52
- /**
53
- * Normalizes a value or array into an array. The returned array MUST be treated
54
- * as read-only — when the input is already an array it is returned as-is (no copy)
55
- * to avoid a per-call allocation on hook hot paths.
56
- */
57
- const toArray = (value) => Array.isArray(value) ? value : [value];
58
- /**+
59
- * Can be used to early return a hook.
60
- *
61
- * If it's an around hook, it will call `next` if provided.
62
- */
63
- const early = (context, next) => {
64
- if (next) return next(context);
65
- };
66
- //#endregion
67
- export { isEmptyObject as a, isPromise as i, hasOwnProperty as n, traverse as o, toArray as r, early as t };
68
-
69
- //# sourceMappingURL=internal.utils-BBB-b6Ud.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"internal.utils-BBB-b6Ud.mjs","names":[],"sources":["../src/common/traverse.ts","../src/common/is-empty-object.ts","../src/common/index.ts","../src/internal.utils.ts"],"sourcesContent":["import _traverse from 'neotraverse'\n\n/**\n * Recursively traverses items (or an array of items) and applies a converter function\n * to every node using `neotraverse`. Modifications happen in place.\n *\n * @example\n * ```ts\n * traverse(items, function () { if (this.key === 'secret') this.remove() })\n * ```\n */\nexport function traverse<T extends Record<string, any>>(\n items: T | T[],\n converter: (item: T) => void,\n) {\n ;(Array.isArray(items) ? items : [items]).forEach((item) => {\n _traverse(item).forEach(converter) // replacement is in place\n })\n}\n","/**\n * Returns `true` only for a plain empty object (`{}`). Arrays, `null`, primitives\n * and non-empty objects are all `false`.\n *\n * @example\n * ```ts\n * isEmptyObject({}) // => true\n * isEmptyObject({ a: 1 }) // => false\n * isEmptyObject([]) // => false\n * ```\n */\nexport const isEmptyObject = (obj: unknown): boolean =>\n !!obj &&\n typeof obj === 'object' &&\n !Array.isArray(obj) &&\n Object.keys(obj).length === 0\n\nif (import.meta.vitest) {\n const { describe, it, expect } = import.meta.vitest\n\n describe('isEmptyObject', () => {\n it('is true only for a plain empty object', () => {\n expect(isEmptyObject({})).toBe(true)\n })\n\n it('is false for a non-empty object', () => {\n expect(isEmptyObject({ a: 1 })).toBe(false)\n })\n\n it('is false for arrays, null, undefined and primitives', () => {\n expect(isEmptyObject([])).toBe(false)\n expect(isEmptyObject(null)).toBe(false)\n expect(isEmptyObject(undefined)).toBe(false)\n expect(isEmptyObject('')).toBe(false)\n expect(isEmptyObject(0)).toBe(false)\n })\n })\n}\n","/**\n * Type guard that checks if a value is a `Promise` instance.\n *\n * @example\n * ```ts\n * const result = maybeSyncFn()\n * if (isPromise(result)) {\n * await result\n * }\n * ```\n */\nexport function isPromise(p: any): p is Promise<any> {\n return p instanceof Promise\n}\n\nexport { traverse } from './traverse.js'\nexport { clone } from './clone.js'\nexport { isEmptyObject } from './is-empty-object.js'\n","import type { HookContext } from '@feathersjs/feathers'\n\nexport const hasOwnProperty = (\n obj: Record<string, unknown>,\n ...keys: string[]\n): boolean => {\n return keys.some((x) => Object.prototype.hasOwnProperty.call(obj, x))\n}\n\nexport type MaybeArray<T> = T | readonly T[]\nexport type UnpackMaybeArray<T> = T extends readonly (infer E)[] ? E : T\n/**\n * Normalizes a value or array into an array. The returned array MUST be treated\n * as read-only — when the input is already an array it is returned as-is (no copy)\n * to avoid a per-call allocation on hook hot paths.\n */\nexport const toArray = <T>(value: T | readonly T[]): T[] =>\n Array.isArray(value) ? (value as T[]) : [value as T]\n\nexport type Promisable<T> = T | Promise<T>\nexport type KeyOf<T> = Extract<keyof T, string>\n\nexport type UnwrapArray<T> = T extends Array<infer U> ? U : T\n\nexport type IsAny<T> = 0 extends 1 & T ? true : false\n\nexport type AnyFallback<T, Fallback> = IsAny<T> extends true ? Fallback : T\n\nexport type NeverFallback<Never, Fallback> = [Never] extends [never]\n ? Fallback\n : Never\n\nexport type KeyOfOrDotNotation<D> = KeyOf<D> | `${KeyOf<D>}.${string}`\n\n/**+\n * Can be used to early return a hook.\n *\n * If it's an around hook, it will call `next` if provided.\n */\nexport const early = <H extends HookContext>(\n context: H,\n next?: (context: H) => Promisable<void>,\n): Promisable<void> => {\n if (next) {\n return next(context)\n }\n return\n}\n"],"mappings":";;;;;;;;;;;AAWA,SAAgB,SACd,OACA,WACA;CACC,CAAC,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,EAAA,CAAG,SAAS,SAAS;EAC1D,UAAU,IAAI,CAAC,CAAC,QAAQ,SAAS;CACnC,CAAC;AACH;;;;;;;;;;;;;;ACPA,MAAa,iBAAiB,QAC5B,CAAC,CAAC,OACF,OAAO,QAAQ,YACf,CAAC,MAAM,QAAQ,GAAG,KAClB,OAAO,KAAK,GAAG,CAAC,CAAC,WAAW;;;;;;;;;;;;;;ACJ9B,SAAgB,UAAU,GAA2B;CACnD,OAAO,aAAa;AACtB;;;ACXA,MAAa,kBACX,KACA,GAAG,SACS;CACZ,OAAO,KAAK,MAAM,MAAM,OAAO,UAAU,eAAe,KAAK,KAAK,CAAC,CAAC;AACtE;;;;;;AASA,MAAa,WAAc,UACzB,MAAM,QAAQ,KAAK,IAAK,QAAgB,CAAC,KAAU;;;;;;AAsBrD,MAAa,SACX,SACA,SACqB;CACrB,IAAI,MACF,OAAO,KAAK,OAAO;AAGvB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mutate-result.util-C0nY6L7i.mjs","names":[],"sources":["../src/utils/get-data-is-array/get-data-is-array.util.ts","../src/utils/get-result-is-array/get-result-is-array.util.ts","../src/utils/replace-data/replace-data.util.ts","../src/utils/mutate-data/mutate-data.util.ts","../src/utils/replace-result/replace-result.util.ts","../src/utils/mutate-result/mutate-result.util.ts"],"sourcesContent":["import type { HookContext } from '@feathersjs/feathers'\nimport type { DataSingleHookContext } from '../../utility-types/hook-context.js'\n\nexport type GetDataIsArrayReturn<H extends HookContext = HookContext> = {\n isArray: boolean\n data: DataSingleHookContext<H>[]\n}\n\n/**\n * Normalizes `context.data` into an array for uniform processing.\n * Returns `{ data, isArray }` where `data` is always an array and `isArray` indicates\n * whether the original value was already an array.\n *\n * @example\n * ```ts\n * import { getDataIsArray } from 'feathers-utils/utils'\n *\n * const { data, isArray } = getDataIsArray(context)\n * data.forEach(item => { /* process each item *\\/ })\n * ```\n *\n * @see https://utils.feathersjs.com/utils/get-data-is-array.html\n */\nexport function getDataIsArray<H extends HookContext = HookContext>(\n context: H,\n): GetDataIsArrayReturn<H> {\n if (!context.data) {\n return {\n isArray: false,\n data: [],\n }\n }\n\n const isArray = Array.isArray(context.data)\n\n return {\n isArray,\n data: isArray ? context.data : [context.data],\n }\n}\n","import type { HookContext } from '@feathersjs/feathers'\nimport { copy } from 'fast-copy'\nimport type { ResultSingleHookContext } from '../../utility-types/hook-context.js'\n\nexport type GetResultIsArrayOptions = {\n dispatch?: boolean\n}\n\nexport type GetResultIsArrayReturn<H extends HookContext = HookContext> = {\n isArray: boolean\n result: ResultSingleHookContext<H>[]\n key: 'dispatch' | 'result'\n}\n\n/**\n * Normalizes `context.result` (or `context.dispatch`) into an array for uniform processing.\n * Handles paginated results by extracting the `data` array. Returns `{ result, isArray, key }`\n * where `key` indicates whether `'result'` or `'dispatch'` was used.\n *\n * @example\n * ```ts\n * import { getResultIsArray } from 'feathers-utils/utils'\n *\n * const { result, isArray } = getResultIsArray(context)\n * result.forEach(item => { /* process each item *\\/ })\n * ```\n *\n * @see https://utils.feathersjs.com/utils/get-result-is-array.html\n */\nexport function getResultIsArray<H extends HookContext = HookContext>(\n context: H,\n options?: GetResultIsArrayOptions,\n): GetResultIsArrayReturn<H> {\n const { dispatch = false } = options || {}\n\n const isDispatch: boolean = dispatch && context.dispatch !== undefined\n\n const result = dispatch\n ? isDispatch\n ? context.dispatch\n : copy(context.result)\n : context.result\n\n if (!result) {\n return {\n isArray: false,\n result: [],\n key: isDispatch ? 'dispatch' : 'result',\n }\n }\n\n const items = context.method === 'find' ? result.data || result : result\n\n const isArray = Array.isArray(items)\n\n return {\n isArray,\n result: isArray ? items : items ? [items] : [],\n key: isDispatch ? 'dispatch' : 'result',\n }\n}\n","import type { HookContext } from \"@feathersjs/feathers\";\nimport type { DataSingleHookContext } from \"../../utility-types/hook-context.js\";\n\n/**\n * Replaces `context.data` wholesale with the given items, preserving the original\n * single-vs-array shape. This is the explicit inverse of `getDataIsArray`: get the\n * data as an array, modify or replace the items, then write them back.\n *\n * @example\n * ```ts\n * import { getDataIsArray, replaceData } from 'feathers-utils/utils'\n *\n * const { data } = getDataIsArray(context)\n * const next = data.map((item) => ({ ...item, slug: slugify(item.name) }))\n * replaceData(context, next)\n * ```\n *\n * @see https://utils.feathersjs.com/utils/replace-data.html\n */\nexport function replaceData<H extends HookContext = HookContext>(\n context: H,\n data: DataSingleHookContext<H>[],\n): H {\n context.data = Array.isArray(context.data) ? data : data[0];\n return context;\n}\n","import type { HookContext } from '@feathersjs/feathers'\nimport { getDataIsArray } from '../get-data-is-array/get-data-is-array.util.js'\nimport { replaceData } from '../replace-data/replace-data.util.js'\nimport { isPromise } from '../../common/index.js'\nimport type { Promisable } from '../../internal.utils.js'\nimport type { TransformerInputFn } from '../../types.js'\n\n/**\n * Applies a transformer function to each item in `context.data`, updating it in place.\n * If the transformer returns a new object, it replaces the original item.\n * Correctly handles both single-item and array data, preserving the original shape.\n *\n * @example\n * ```ts\n * import { mutateData } from 'feathers-utils/utils'\n *\n * await mutateData(context, (item) => { item.name = item.name.trim() })\n * ```\n *\n * @see https://utils.feathersjs.com/utils/mutate-data.html\n */\nexport function mutateData<H extends HookContext = HookContext>(\n context: H,\n transformer: TransformerInputFn<any, H>,\n): Promisable<H> {\n if (!context.data) {\n return context\n }\n\n // single-item fast path: avoid allocating a wrapper array + a mapped array\n // for the common single create/update/patch case.\n if (!Array.isArray(context.data)) {\n const item = context.data\n const result = transformer(item, { context, i: 0 })\n\n if (isPromise(result)) {\n return result.then((res: any) => {\n context.data = res ?? item\n return context\n })\n }\n\n context.data = result ?? item\n return context\n }\n\n const { data } = getDataIsArray(context)\n\n if (!data.length) {\n return context\n }\n\n let hasPromises = false\n\n const results = data.map((item, i) => {\n const result = transformer(item, { context, i })\n\n if (isPromise(result)) {\n hasPromises = true\n return result.then((res: any) => res ?? item)\n }\n\n return result ?? item\n })\n\n function mutate(data: any) {\n // delegate the array writeback (single is handled by the fast path above)\n return replaceData(context, data)\n }\n\n if (hasPromises) {\n return Promise.all(results).then(mutate)\n } else {\n return mutate(results)\n }\n}\n","import type { HookContext } from \"@feathersjs/feathers\";\nimport { copy } from \"fast-copy\";\nimport { getResultIsArray } from \"../get-result-is-array/get-result-is-array.util.js\";\nimport type { DispatchOption } from \"../../types.js\";\nimport type { ResultSingleHookContext } from \"../../utility-types/hook-context.js\";\n\nexport type ReplaceResultOptions = {\n /**\n * Also (or only) write to `context.dispatch`. `true` writes dispatch, `'both'`\n * writes both `result` and `dispatch`. When dispatch is requested and not yet\n * present, it is seeded from a clone of `context.result`.\n */\n dispatch?: DispatchOption;\n};\n\n/**\n * Replaces `context.result` (and/or `context.dispatch`) wholesale with the given\n * items, preserving the original shape: single item, array, or paginated `{ data }`.\n * This is the explicit inverse of `getResultIsArray`.\n *\n * @example\n * ```ts\n * import { getResultIsArray, replaceResult } from 'feathers-utils/utils'\n *\n * const { result } = getResultIsArray(context)\n * replaceResult(context, result.filter((item) => item.public))\n * ```\n *\n * @see https://utils.feathersjs.com/utils/replace-result.html\n */\nexport function replaceResult<H extends HookContext = HookContext>(\n context: H,\n result: ResultSingleHookContext<H>[],\n options?: ReplaceResultOptions,\n): H {\n if (!!options?.dispatch && !context.dispatch) {\n context.dispatch = copy(context.result);\n }\n\n const write = (dispatch: boolean) => {\n const { isArray, key } = getResultIsArray(context, { dispatch });\n\n if (!isArray) {\n context[key] = result[0];\n } else if (!Array.isArray(context[key]) && context[key]?.data) {\n context[key].data = result;\n } else {\n context[key] = result;\n }\n };\n\n if (options?.dispatch === \"both\") {\n write(true);\n write(false);\n } else {\n write(!!options?.dispatch);\n }\n\n return context;\n}\n","import type { HookContext, NextFunction } from '@feathersjs/feathers'\nimport { getResultIsArray } from '../get-result-is-array/get-result-is-array.util.js'\nimport { replaceResult } from '../replace-result/replace-result.util.js'\nimport { isPromise } from '../../common/index.js'\nimport { copy } from 'fast-copy'\nimport type { Promisable } from '../../internal.utils.js'\nimport type { DispatchOption, TransformerInputFn } from '../../types.js'\n\nexport type MutateResultOptions = {\n next?: NextFunction\n transform?: (items: any[]) => any[]\n dispatch?: DispatchOption\n}\n\n/**\n * Applies a transformer function to each item in `context.result` (and optionally `context.dispatch`).\n * Handles paginated results, single items, and arrays transparently.\n * Use the `dispatch` option to control whether result, dispatch, or both are transformed.\n *\n * @example\n * ```ts\n * import { mutateResult } from 'feathers-utils/utils'\n *\n * await mutateResult(context, (item) => { delete item.password })\n * ```\n *\n * @see https://utils.feathersjs.com/utils/mutate-result.html\n */\nexport function mutateResult<H extends HookContext = HookContext>(\n context: H,\n transformer: TransformerInputFn<any, H>,\n options?: MutateResultOptions,\n): Promisable<H> {\n if (!!options?.dispatch && !context.dispatch) {\n context.dispatch = copy(context.result)\n }\n\n function forResult(dispatch: boolean): Promisable<H> {\n const { result } = getResultIsArray(context, { dispatch })\n\n if (!result.length) {\n return context\n }\n\n let hasPromises = false\n\n const results = result.map((item, i) => {\n const result = transformer(item, { context, i })\n\n if (isPromise(result)) {\n hasPromises = true\n return result.then((res: any) => res ?? item)\n }\n\n return result ?? item\n })\n\n function mutate(r: any) {\n if (options?.transform) {\n r = options.transform(r)\n }\n\n // delegate the single/array/paginated writeback to replaceResult\n return replaceResult(context, r, { dispatch })\n }\n\n if (hasPromises) {\n return Promise.all(results).then(mutate)\n } else {\n return mutate(results)\n }\n }\n\n function run(): Promisable<H> {\n if (options?.dispatch === 'both') {\n const a = forResult(true)\n const b = forResult(false)\n\n if (isPromise(a) || isPromise(b)) {\n return Promise.all([a, b]).then(() => context)\n }\n\n return context\n }\n\n return forResult(options?.dispatch ?? false)\n }\n\n if (options?.next) {\n return options.next().then(run)\n }\n\n return run()\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils-DByCpAsf.mjs","names":["deepEqual","deepEqual","deepEqual"],"sources":["../src/utils/sort-query-properties/sort-query-properties.util.ts","../src/utils/add-skip/add-skip.util.ts","../src/utils/add-to-query/add-to-query.util.ts","../src/utils/check-context/check-context.util.ts","../src/utils/chunk-find/chunk-find.util.ts","../src/utils/context-to-json/context-to-json.util.ts","../src/utils/define-hooks/define-hooks.util.ts","../src/feathers-cjs-fix.ts","../src/utils/get-exposed-methods/get-exposed-methods.util.ts","../src/utils/iterate-find/iterate-find.util.ts","../src/utils/merge-query/extract-query-filters.ts","../src/utils/merge-query/logical-branches.ts","../src/utils/merge-query/dedupe-branches.ts","../src/utils/merge-query/has-conflict.ts","../src/utils/merge-query/merge-query-bodies.ts","../src/utils/merge-query/merge-select.ts","../src/utils/merge-query/merge-query.util.ts","../src/utils/patch-batch/patch-batch.util.ts","../src/utils/walk-query/walk-query.util.ts","../src/utils/query-has-property/query-has-property.util.ts","../src/utils/query-defaults/query-defaults.util.ts","../src/utils/skip-result/skip-result.util.ts","../src/utils/to-paginated/to-paginated.util.ts","../src/utils/transform-params/transform-params.util.ts","../src/utils/wait-for-service-event/wait-for-service-event.util.ts","../src/utils/zip-data-result/zip-data-result.util.ts"],"sourcesContent":["import type { Query } from '@feathersjs/feathers'\n\nconst arrayOperators = new Set(['$or', '$and', '$nor', '$not', '$in', '$nin'])\n\nconst isPlainObjectLike = (value: unknown): value is Record<string, any> =>\n value !== null && typeof value === 'object'\n\n/**\n * Recursively normalizes a Feathers query object for order-independent comparison.\n * Sorts object keys and sorts arrays within `$or`, `$and`, `$nor`, `$not`, `$in`,\n * and `$nin` operators so that different orderings produce the same result.\n *\n * This is useful for generating stable cache keys where\n * `{ $or: [{ a: 1 }, { b: 2 }] }` and `{ $or: [{ b: 2 }, { a: 1 }] }`\n * should be treated as equivalent.\n *\n * @example\n * ```ts\n * import { sortQueryProperties } from 'feathers-utils/utils'\n *\n * const normalized = sortQueryProperties({\n * $or: [{ name: 'Jane' }, { name: 'John' }],\n * age: { $in: [30, 25] },\n * })\n * // => { $or: [{ name: 'John' }, { name: 'Jane' }], age: { $in: [25, 30] } }\n * // (sorted by stable stringify comparison)\n * ```\n *\n * @see https://utils.feathersjs.com/utils/sort-query-properties.html\n */\nexport const sortQueryProperties = <Q extends Query>(query: Q): Q => {\n return normalize(query) as Q\n}\n\nconst normalize = (value: any): any => {\n if (Array.isArray(value)) {\n return value.map(normalize)\n }\n\n if (!isPlainObjectLike(value)) {\n return value\n }\n\n const sorted: Record<string, any> = {}\n\n for (const key of Object.keys(value).sort()) {\n const val = value[key]\n\n if (arrayOperators.has(key) && Array.isArray(val)) {\n // Schwartzian transform: serialize each normalized element once, sort by\n // that key, then unwrap. Avoids the O(n log n) repeated JSON.stringify of\n // the previous comparator (which also returned 1 for equal elements).\n sorted[key] = val\n .map((el) => {\n const normalized = normalize(el)\n return { k: JSON.stringify(normalized), v: normalized }\n })\n .sort((a, b) => (a.k < b.k ? -1 : a.k > b.k ? 1 : 0))\n .map((entry) => entry.v)\n } else {\n sorted[key] = normalize(val)\n }\n }\n\n return sorted\n}\n","import type { HookContext, HookType } from '@feathersjs/feathers'\nimport type { MaybeArray } from '../../internal.utils.js'\n\nexport type SkipHookName =\n | 'all'\n | HookType\n | `${HookType}:${string}`\n | (string & {})\n\n/**\n * Adds hook names to `context.params.skipHooks` so that `skippable`-wrapped hooks\n * will be bypassed for the current service call. Accepts a single name or an array.\n * Duplicates are automatically removed.\n *\n * @example\n * ```ts\n * import { addSkip } from 'feathers-utils/utils'\n *\n * // Inside a hook or custom code:\n * addSkip(context, 'myHook')\n * addSkip(context, ['hookA', 'hookB'])\n * ```\n *\n * @see https://utils.feathersjs.com/utils/add-skip.html\n */\nexport const addSkip = <H extends HookContext>(\n context: H,\n hooks: MaybeArray<SkipHookName>,\n) => {\n const names = Array.isArray(hooks) ? hooks : [hooks]\n\n if (context.params.skipHooks === undefined) {\n context.params = {\n ...context.params,\n skipHooks: [...names],\n }\n } else {\n if (!Array.isArray(context.params.skipHooks)) {\n throw new Error('Invalid skipHooks parameter')\n }\n\n context.params = {\n ...context.params,\n skipHooks: [...new Set([...context.params.skipHooks, ...names])],\n }\n }\n}\n","import type { Query } from '@feathersjs/feathers'\nimport { dequal as deepEqual } from 'dequal'\n\n/**\n * Safely merges properties into a Feathers query object. If a property already exists\n * with a different value, it wraps both in a `$and` array to preserve both conditions.\n * If the exact same key-value pair already exists, no changes are made. When the added\n * query is itself a pure `$and` (`{ $and: [...] }`), its branches are flattened into the\n * target's `$and` rather than nested.\n *\n * @example\n * ```ts\n * import { addToQuery } from 'feathers-utils/utils'\n *\n * const query = { status: 'active' }\n * addToQuery(query, { role: 'admin' })\n * // => { status: 'active', role: 'admin' }\n * ```\n *\n * @see https://utils.feathersjs.com/utils/add-to-query.html\n */\nexport function addToQuery<Q extends Query>(targetQuery: Q, query: Q): Q {\n targetQuery ??= {} as Q\n\n if (Object.keys(query).length === 0) {\n return targetQuery\n }\n\n const entries = Object.entries(query) as [keyof Q, any][]\n\n if (entries.every(([property]) => !(property in targetQuery))) {\n // if none of the properties exist, merge them directly\n return {\n ...targetQuery,\n ...query,\n }\n }\n\n function isAlreadyInQuery(targetQuery: Q, entries: [keyof Q, any][]) {\n return entries.every(\n ([property, value]) =>\n property in targetQuery && deepEqual(targetQuery[property], value),\n )\n }\n\n if (isAlreadyInQuery(targetQuery, entries)) {\n // if all properties already exist with the exact same value, do nothing\n return targetQuery\n }\n\n // when the added query is itself a pure `$and`, flatten its branches into the\n // target's `$and` instead of nesting another `$and` inside it\n if (entries.length === 1 && Array.isArray(query.$and)) {\n const existing = (targetQuery.$and as any[]) ?? []\n const newBranches = (query.$and as any[]).filter(\n (branch) => !existing.some((q) => deepEqual(q, branch)),\n )\n if (newBranches.length === 0) {\n return targetQuery\n }\n return {\n ...targetQuery,\n $and: [...existing, ...newBranches],\n }\n }\n\n if (!targetQuery.$and) {\n return {\n ...targetQuery,\n $and: [{ ...query }],\n }\n }\n\n // check if the exact same value already exists in $and\n if (targetQuery.$and.some((q: any) => isAlreadyInQuery(q, entries))) {\n return targetQuery\n }\n\n return {\n ...targetQuery,\n $and: [...targetQuery.$and, { ...query }],\n }\n}\n","import type { HookContext } from '@feathersjs/feathers'\nimport type { HookType, MethodName } from '../../types.js'\nimport {\n isContext,\n type IsContextOptions,\n} from '../../predicates/is-context/is-context.predicate.js'\nimport type { UnpackMaybeArray } from '../../internal.utils.js'\n\ntype NarrowedContext<H extends HookContext, O> = H &\n (O extends { path: infer P }\n ? [P] extends [undefined | null]\n ? unknown\n : { path: UnpackMaybeArray<P> }\n : unknown) &\n (O extends { type: infer T }\n ? [T] extends [undefined | null]\n ? unknown\n : { type: UnpackMaybeArray<T> }\n : unknown) &\n (O extends { method: infer M }\n ? [M] extends [undefined | null]\n ? unknown\n : { method: UnpackMaybeArray<M> }\n : unknown)\n\nexport type CheckContextOptions<H extends HookContext = HookContext> =\n IsContextOptions<H> & {\n label?: string\n }\n\n/**\n * Validates that the hook context matches the expected type(s) and method(s).\n * Throws an error if the context is invalid, preventing hooks from running in\n * unsupported configurations. Typically used internally by other hooks.\n * Also narrows the context type based on the passed options.\n *\n * @example\n * ```ts\n * import { checkContext } from 'feathers-utils/utils'\n *\n * const myHook = (context) => {\n * checkContext(context, ['before', 'around'], ['create', 'patch'], 'myHook')\n * // or with options object:\n * checkContext(context, { type: ['before', 'around'], method: ['create', 'patch'], label: 'myHook' })\n * // context.type is now 'before' | 'around', context.method is now 'create' | 'patch'\n * }\n * ```\n *\n * @see https://utils.feathersjs.com/utils/check-context.html\n */\nexport function checkContext<\n H extends HookContext,\n const O extends CheckContextOptions<NoInfer<H>>,\n>(context: H, options: O): asserts context is NarrowedContext<H, O>\nexport function checkContext<\n H extends HookContext,\n const T extends HookType | HookType[] | null | undefined = undefined,\n const M extends MethodName | MethodName[] | null | undefined = undefined,\n>(\n context: H,\n type?: T,\n methods?: M,\n label?: string,\n): asserts context is NarrowedContext<H, { type: T; method: M }>\nexport function checkContext<H extends HookContext = HookContext>(\n context: H,\n typeOrOptions?:\n | HookType\n | HookType[]\n | CheckContextOptions<NoInfer<H>>\n | null,\n methods?: MethodName | MethodName[] | null,\n label = 'anonymous',\n): void {\n let options: IsContextOptions\n let hookLabel: string\n\n if (\n typeOrOptions != null &&\n typeof typeOrOptions === 'object' &&\n !Array.isArray(typeOrOptions)\n ) {\n const { label: optLabel, ...rest } = typeOrOptions\n options = rest\n hookLabel = optLabel ?? 'anonymous'\n } else {\n options = {\n method: methods ?? undefined,\n type: typeOrOptions ?? undefined,\n }\n hookLabel = label\n }\n\n if (!isContext(options)(context)) {\n const details: string[] = []\n\n if (options.type != null) {\n details.push(\n `type: expected '${Array.isArray(options.type) ? options.type.join(\"' | '\") : options.type}' but got '${context.type}'`,\n )\n }\n if (options.method != null) {\n details.push(\n `method: expected '${Array.isArray(options.method) ? options.method.join(\"' | '\") : options.method}' but got '${context.method}'`,\n )\n }\n if (options.path != null) {\n details.push(\n `path: expected '${Array.isArray(options.path) ? options.path.join(\"' | '\") : options.path}' but got '${context.path}'`,\n )\n }\n\n throw new Error(\n `The '${hookLabel}' hook has invalid context (${details.join(', ')}).`,\n )\n }\n}\n","import type { Application, Params } from '@feathersjs/feathers'\nimport type { KeyOf } from '../../internal.utils.js'\nimport type {\n InferFindParams,\n InferFindResultSingle,\n} from '../../utility-types/infer-service-methods.js'\n\ntype PaginateOption = { default?: number; max?: number }\n\ntype ChunkFindOptions<P extends Params = Params> = {\n params?: P & { paginate?: PaginateOption }\n}\n\n/**\n * Use `for await` to iterate over chunks (pages) of results from a `find` method.\n *\n * This function is useful for processing large datasets in batches without loading everything into memory at once.\n * It uses pagination to fetch results in chunks, yielding each page's data array.\n *\n * @example\n * ```ts\n * import { chunkFind } from 'feathers-utils/utils'\n *\n * const app = feathers()\n *\n * // Assuming 'users' service has many records\n * for await (const users of chunkFind(app, 'users', {\n * params: { query: { active: true }, // Custom query parameters\n * } })) {\n * console.log(users) // Process each chunk of user records\n * }\n * ```\n *\n * @see https://utils.feathersjs.com/utils/chunk-find.html\n */\nexport async function* chunkFind<\n Services,\n Path extends KeyOf<Services>,\n Service extends Services[Path] = Services[Path],\n P extends Params = InferFindParams<Service>,\n Item = InferFindResultSingle<Service>,\n>(\n app: Application<Services>,\n servicePath: Path,\n options?: ChunkFindOptions<P>,\n): AsyncGenerator<Item[], void, unknown> {\n const service = app.service(servicePath)\n\n if (!service || !('find' in service)) {\n throw new Error(`Service '${servicePath}' does not have a 'find' method.`)\n }\n\n const params = {\n ...options?.params,\n query: {\n ...(options?.params?.query ?? {}),\n $limit: options?.params?.query?.$limit ?? 10,\n $skip: options?.params?.query?.$skip ?? 0,\n },\n paginate: {\n default: options?.params?.paginate?.default ?? 10,\n max: options?.params?.paginate?.max ?? 100,\n },\n }\n\n let result\n\n do {\n result = await (service as any).find(params)\n\n // Guard against an infinite loop: an empty page never advances $skip, so\n // `total > $skip` could stay true forever (e.g. $limit:0, or a stale total\n // when items are concurrently removed / filtered out by hooks).\n if (!result.data.length) {\n break\n }\n\n yield result.data\n\n params.query.$skip = (params.query.$skip ?? 0) + result.data.length\n } while (result.total > params.query.$skip)\n}\n","import type { HookContext } from '@feathersjs/feathers'\n\n/**\n * Converts a FeathersJS HookContext to a plain JSON object by calling `toJSON()` if available.\n * This is important when using lodash `get`/`has` on the context, since the HookContext\n * class uses getters that may not be enumerable.\n *\n * @example\n * ```ts\n * import { contextToJson } from 'feathers-utils/utils'\n *\n * const json = contextToJson(context)\n * console.log(json)\n * ```\n *\n * @see https://utils.feathersjs.com/utils/context-to-json.html\n */\nexport const contextToJson = (context: HookContext) => {\n if (context.toJSON) {\n return context.toJSON()\n }\n return context\n}\n","import type { Application, HookOptions } from '@feathersjs/feathers'\n\n/**\n * TypeScript helper that provides full type inference and autocompletion when defining\n * service hooks. It is an identity function that simply returns its input,\n * but enables your IDE to infer the correct hook context types.\n *\n * @example\n * ```ts\n * import { defineHooks } from 'feathers-utils/utils'\n *\n * export const userHooks = defineHooks({\n * before: { create: [validateUser()] },\n * after: { all: [sanitizeResult()] }\n * })\n * ```\n *\n * @see https://utils.feathersjs.com/utils/define-hooks.html\n */\nexport function defineHooks<\n A extends Application = Application,\n S = {\n find: any\n get: any\n create: any\n update: any\n patch: any\n remove: any\n },\n Options = HookOptions<A, S>,\n>(hooks: Options): Options {\n return hooks\n}\n","// src/feathers.ts\nimport * as feathers from '@feathersjs/feathers'\n\n// Type-safe re-export of only what you need\nexport const SERVICE = (feathers as any).SERVICE || feathers.default?.SERVICE\n","import type { Service } from '@feathersjs/feathers'\nimport { SERVICE } from '../../feathers-cjs-fix.js'\n\n/**\n * Returns the list of method names that are publicly exposed by a Feathers service.\n * Reads the internal `[SERVICE].methods` property set during service registration.\n * Throws if the service does not have any exposed methods configured.\n *\n * @example\n * ```ts\n * import { getExposedMethods } from 'feathers-utils/utils'\n *\n * const methods = getExposedMethods(app.service('users'))\n * // => ['find', 'get', 'create', 'patch', 'remove']\n * ```\n *\n * @see https://utils.feathersjs.com/utils/get-exposed-methods.html\n */\nexport function getExposedMethods(service: Service) {\n const result = (service as any)[SERVICE].methods\n\n if (!result || !Array.isArray(result)) {\n throw new Error(`Service does not have exposed methods`)\n }\n\n return result\n}\n","import type { Application, Params } from '@feathersjs/feathers'\nimport type { KeyOf } from '../../internal.utils.js'\nimport type {\n InferFindParams,\n InferFindResultSingle,\n} from '../../utility-types/infer-service-methods.js'\n\ntype PaginateOption = { default?: number; max?: number }\n\ntype IterateFindOptions<P extends Params = Params> = {\n params?: P & { paginate?: PaginateOption }\n}\n\n/**\n * Use `for await` to iterate over the results of a `find` method.\n *\n * This function is useful for iterating over large datasets without loading everything into memory at once.\n * It uses pagination to fetch results in chunks, allowing you to process each item as it is retrieved.\n *\n * @example\n * ```ts\n * import { iterateFind } from 'feathers-utils/utils'\n *\n * const app = feathers()\n *\n * // Assuming 'users' service has many records\n * for await (const user of iterateFind(app, 'users', {\n * params: { query: { active: true }, // Custom query parameters\n * } })) {\n * console.log(user) // Process each user record\n * }\n * ```\n *\n * @see https://utils.feathersjs.com/utils/iterate-find.html\n */\nexport async function* iterateFind<\n Services,\n Path extends KeyOf<Services>,\n Service extends Services[Path] = Services[Path],\n P extends Params = InferFindParams<Service>,\n Item = InferFindResultSingle<Service>,\n>(\n app: Application<Services>,\n servicePath: Path,\n options?: IterateFindOptions<P>,\n): AsyncGenerator<Item, void, unknown> {\n const service = app.service(servicePath)\n\n if (!service || !('find' in service)) {\n throw new Error(`Service '${servicePath}' does not have a 'find' method.`)\n }\n\n const params = {\n ...options?.params,\n query: {\n ...(options?.params?.query ?? {}),\n $limit: options?.params?.query?.$limit,\n $skip: options?.params?.query?.$skip ?? 0,\n },\n paginate: {\n default: options?.params?.paginate?.default ?? 10,\n max: options?.params?.paginate?.max ?? 100,\n },\n }\n\n let result\n\n do {\n result = await (service as any).find(params)\n\n // Guard against an infinite loop: an empty page never advances $skip, so\n // `total > $skip` could stay true forever (e.g. $limit:0, or a stale total\n // when items are concurrently removed / filtered out by hooks).\n if (!result.data.length) {\n break\n }\n\n for (const item of result.data) {\n yield item\n }\n\n params.query.$skip = (params.query.$skip ?? 0) + result.data.length\n } while (result.total > params.query.$skip)\n}\n","import type { Query } from '@feathersjs/feathers'\n\nexport type FilterQueryResult<Q extends Query = Query> = {\n $select?: Q['$select']\n $limit?: Q['$limit']\n $skip?: Q['$skip']\n $sort?: Q['$sort']\n query: Omit<Q, '$select' | '$limit' | '$skip' | '$sort'>\n}\n\n/**\n * Splits a query into its special filters ($select, $limit, $skip, $sort) and the\n * remaining query body. Internal helper for {@link mergeQuery} — not part of the\n * public API.\n */\nexport function extractQueryFilters<Q extends Query>(\n providedQuery?: Q,\n): FilterQueryResult<Q> {\n providedQuery ??= {} as Q\n const { $select, $limit, $skip, $sort, ...query } = providedQuery\n\n const result: FilterQueryResult<Q> = { query } as FilterQueryResult<Q>\n\n if ('$select' in providedQuery) {\n result.$select = $select\n }\n\n if ('$limit' in providedQuery) {\n result.$limit = $limit\n }\n\n if ('$skip' in providedQuery) {\n result.$skip = $skip\n }\n\n if ('$sort' in providedQuery) {\n result.$sort = $sort\n }\n\n return result\n}\n\nif (import.meta.vitest) {\n const { describe, it, expect } = import.meta.vitest\n\n describe('extractQueryFilters', () => {\n it('splits filters from the query body', () => {\n expect(\n extractQueryFilters({\n $select: ['a'],\n $limit: 10,\n $skip: 10,\n $sort: { a: 1 },\n a: 1,\n b: 2,\n }),\n ).toEqual({\n $select: ['a'],\n $limit: 10,\n $skip: 10,\n $sort: { a: 1 },\n query: { a: 1, b: 2 },\n })\n })\n\n it('omits filters that are not provided', () => {\n expect(extractQueryFilters({ a: 1, b: 2 })).toEqual({\n query: { a: 1, b: 2 },\n })\n })\n\n it('returns an empty body for an empty query', () => {\n expect(extractQueryFilters({})).toEqual({ query: {} })\n })\n\n it('returns an empty body for undefined', () => {\n expect(extractQueryFilters(undefined)).toEqual({ query: {} })\n })\n })\n}\n","type QueryRecord = Record<string, any>\n\n/**\n * Returns the branches of a logical-only query (a query whose single key is `op`),\n * or `null` when the query is not purely `{ [op]: [...] }`. Internal helper for\n * {@link mergeQuery}.\n */\nexport function logicalBranches(\n query: QueryRecord,\n op: '$or' | '$and',\n): QueryRecord[] | null {\n const keys = Object.keys(query)\n if (keys.length === 1 && keys[0] === op && Array.isArray(query[op])) {\n return query[op] as QueryRecord[]\n }\n return null\n}\n\nif (import.meta.vitest) {\n const { describe, it, expect } = import.meta.vitest\n\n describe('logicalBranches', () => {\n it('returns branches for a logical-only query', () => {\n expect(logicalBranches({ $or: [{ id: 1 }] }, '$or')).toEqual([{ id: 1 }])\n })\n\n it('returns null when the operator is mixed with other keys', () => {\n expect(logicalBranches({ $or: [{ id: 1 }], a: 1 }, '$or')).toBeNull()\n })\n\n it('returns null for the wrong operator', () => {\n expect(logicalBranches({ $and: [{ id: 1 }] }, '$or')).toBeNull()\n })\n\n it('returns null when the operator value is not an array', () => {\n expect(logicalBranches({ $or: { id: 1 } }, '$or')).toBeNull()\n })\n })\n}\n","import { dequal as deepEqual } from 'dequal'\nimport { isEmptyObject } from '../../common/is-empty-object.js'\n\ntype QueryRecord = Record<string, any>\n\n/**\n * Removes empty (`{}`) and deep-equal duplicate branches, preserving order.\n * Internal helper for {@link mergeQuery}.\n */\nexport function dedupeBranches(branches: QueryRecord[]): QueryRecord[] {\n const result: QueryRecord[] = []\n for (const branch of branches) {\n if (isEmptyObject(branch)) {\n continue\n }\n if (!result.some((existing) => deepEqual(existing, branch))) {\n result.push(branch)\n }\n }\n return result\n}\n\nif (import.meta.vitest) {\n const { describe, it, expect } = import.meta.vitest\n\n describe('dedupeBranches', () => {\n it('removes empty objects and deep-equal duplicates', () => {\n expect(dedupeBranches([{ id: 1 }, {}, { id: 1 }, { id: 2 }])).toEqual([\n { id: 1 },\n { id: 2 },\n ])\n })\n\n it('preserves order', () => {\n expect(dedupeBranches([{ b: 2 }, { a: 1 }])).toEqual([{ b: 2 }, { a: 1 }])\n })\n\n it('returns an empty array when all branches are empty', () => {\n expect(dedupeBranches([{}, {}])).toEqual([])\n })\n })\n}\n","import { dequal as deepEqual } from 'dequal'\n\ntype QueryRecord = Record<string, any>\n\n/**\n * Two query bodies conflict when they share at least one key whose values are not\n * deep-equal. Internal helper for {@link mergeQuery}.\n */\nexport function hasConflict(target: QueryRecord, source: QueryRecord): boolean {\n for (const key of Object.keys(target)) {\n if (key in source && !deepEqual(target[key], source[key])) {\n return true\n }\n }\n return false\n}\n\nif (import.meta.vitest) {\n const { describe, it, expect } = import.meta.vitest\n\n describe('hasConflict', () => {\n it('is false for disjoint keys', () => {\n expect(hasConflict({ a: 1 }, { b: 2 })).toBe(false)\n })\n\n it('is false for shared equal values', () => {\n expect(hasConflict({ a: 1 }, { a: 1, b: 2 })).toBe(false)\n })\n\n it('is true for shared differing values', () => {\n expect(hasConflict({ a: 1 }, { a: 2 })).toBe(true)\n })\n\n it('compares values deeply', () => {\n expect(hasConflict({ a: { x: 1 } }, { a: { x: 1 } })).toBe(false)\n expect(hasConflict({ a: { x: 1 } }, { a: { x: 2 } })).toBe(true)\n })\n })\n}\n","import type { MergeQueryMode } from './merge-query.util.js'\nimport { isEmptyObject } from '../../common/is-empty-object.js'\nimport { logicalBranches } from './logical-branches.js'\nimport { dedupeBranches } from './dedupe-branches.js'\nimport { hasConflict } from './has-conflict.js'\n\ntype QueryRecord = Record<string, any>\n\n/**\n * Merges two query bodies (filters already removed) according to the mode.\n * Internal helper for {@link mergeQuery}.\n *\n * - `target` / `source`: precedence merge (that side wins on conflict).\n * - `combine`: the two bodies always become branches of a single `$or`.\n * - `intersect`: non-conflicting bodies merge flat; on conflict they become\n * branches of a single `$and`.\n *\n * Logical-only bodies (`{ $or: [...] }` for combine, `{ $and: [...] }` for\n * intersect) are flattened into the result and their branches de-duplicated.\n */\nexport function mergeQueryBodies(\n target: QueryRecord,\n source: QueryRecord,\n mode: MergeQueryMode,\n): QueryRecord {\n if (mode === 'target') {\n return { ...source, ...target }\n }\n if (mode === 'source') {\n return { ...target, ...source }\n }\n\n if (isEmptyObject(target)) {\n return { ...source }\n }\n if (isEmptyObject(source)) {\n return { ...target }\n }\n\n const op = mode === 'combine' ? '$or' : '$and'\n\n const targetBranches = logicalBranches(target, op)\n const sourceBranches = logicalBranches(source, op)\n\n // For intersect (AND) the top level is itself an implicit AND, so two\n // conflict-free bodies can be merged flat. For combine (OR) there is no flat\n // representation — combine always produces an `$or`.\n if (\n op === '$and' &&\n !targetBranches &&\n !sourceBranches &&\n !hasConflict(target, source)\n ) {\n return { ...target, ...source }\n }\n\n const branches = dedupeBranches([\n ...(targetBranches ?? [target]),\n ...(sourceBranches ?? [source]),\n ])\n\n if (branches.length === 0) {\n return {}\n }\n if (branches.length === 1) {\n return { ...branches[0] }\n }\n return { [op]: branches }\n}\n\nif (import.meta.vitest) {\n const { describe, it, expect } = import.meta.vitest\n\n describe('mergeQueryBodies', () => {\n it('target / source precedence', () => {\n expect(mergeQueryBodies({ id: 1 }, { id: 2, a: 3 }, 'target')).toEqual({\n id: 1,\n a: 3,\n })\n expect(mergeQueryBodies({ id: 1 }, { id: 2, a: 3 }, 'source')).toEqual({\n id: 2,\n a: 3,\n })\n })\n\n it('returns the other side when one is empty', () => {\n expect(mergeQueryBodies({}, { id: 1 }, 'combine')).toEqual({ id: 1 })\n expect(mergeQueryBodies({ id: 1 }, {}, 'intersect')).toEqual({ id: 1 })\n })\n\n it('combine always produces an $or, even for disjoint keys', () => {\n expect(mergeQueryBodies({ a: 1 }, { b: 2 }, 'combine')).toEqual({\n $or: [{ a: 1 }, { b: 2 }],\n })\n })\n\n it('combine flattens and dedupes $or branches', () => {\n expect(\n mergeQueryBodies(\n { $or: [{ id: 1 }, { id: 2 }] },\n { $or: [{ id: 2 }, { id: 3 }] },\n 'combine',\n ),\n ).toEqual({ $or: [{ id: 1 }, { id: 2 }, { id: 3 }] })\n })\n\n it('combine collapses to a single body', () => {\n expect(mergeQueryBodies({ id: 1 }, { id: 1 }, 'combine')).toEqual({\n id: 1,\n })\n })\n\n it('intersect merges disjoint keys flat', () => {\n expect(mergeQueryBodies({ id: 1 }, { userId: 2 }, 'intersect')).toEqual({\n id: 1,\n userId: 2,\n })\n })\n\n it('intersect wraps conflicts in $and', () => {\n expect(mergeQueryBodies({ id: 1 }, { id: 2 }, 'intersect')).toEqual({\n $and: [{ id: 1 }, { id: 2 }],\n })\n })\n\n it('intersect flattens $and branches', () => {\n expect(\n mergeQueryBodies(\n { $and: [{ id: 1 }, { id: 2 }] },\n { $and: [{ id: 3 }] },\n 'intersect',\n ),\n ).toEqual({ $and: [{ id: 1 }, { id: 2 }, { id: 3 }] })\n })\n })\n}\n","import type { MergeQueryMode } from './merge-query.util.js'\n\n/**\n * Merges two `$select` filters according to the mode: `combine` → union,\n * `intersect` → intersection, `target`/`source` → that side. When only one side\n * provides a `$select`, that one is used. Internal helper for {@link mergeQuery}.\n */\nexport function mergeSelect(\n target: any,\n source: any,\n mode: MergeQueryMode,\n): any {\n if (target === undefined) {\n return source\n }\n if (source === undefined) {\n return target\n }\n if (mode === 'target') {\n return target\n }\n if (mode === 'source') {\n return source\n }\n const targetArr = Array.isArray(target) ? target : [target]\n const sourceArr = Array.isArray(source) ? source : [source]\n if (mode === 'combine') {\n return [...new Set([...targetArr, ...sourceArr])]\n }\n // intersect\n return targetArr.filter((value) => sourceArr.includes(value))\n}\n\nif (import.meta.vitest) {\n const { describe, it, expect } = import.meta.vitest\n\n describe('mergeSelect', () => {\n it('returns the defined side when one is missing', () => {\n expect(mergeSelect(undefined, ['a'], 'combine')).toEqual(['a'])\n expect(mergeSelect(['a'], undefined, 'combine')).toEqual(['a'])\n })\n\n it('unions on combine', () => {\n expect(mergeSelect(['a', 'b'], ['b', 'c'], 'combine')).toEqual([\n 'a',\n 'b',\n 'c',\n ])\n })\n\n it('intersects on intersect', () => {\n expect(mergeSelect(['a', 'b'], ['b', 'c'], 'intersect')).toEqual(['b'])\n })\n\n it('can produce an empty intersection', () => {\n expect(mergeSelect(['a'], ['b'], 'intersect')).toEqual([])\n })\n\n it('picks the requested side on target / source', () => {\n expect(mergeSelect(['a'], ['b'], 'target')).toEqual(['a'])\n expect(mergeSelect(['a'], ['b'], 'source')).toEqual(['b'])\n })\n })\n}\n","import type { Query } from '@feathersjs/feathers'\nimport { extractQueryFilters } from './extract-query-filters.js'\nimport { mergeQueryBodies } from './merge-query-bodies.js'\nimport { mergeSelect } from './merge-select.js'\n\nexport type MergeQueryMode = 'target' | 'source' | 'combine' | 'intersect'\n\nexport interface MergeQueryOptions {\n /**\n * How to merge query properties that both queries constrain.\n *\n * - `combine` (default): broaden — the two queries always become branches of an `$or`.\n * - `intersect`: narrow — non-conflicting properties merge flat, conflicts become an `$and`.\n * - `target`: keep the target's value on conflict.\n * - `source`: keep the source's value on conflict.\n */\n mode?: MergeQueryMode\n}\n\n/**\n * Properties are combined with a logical operator rather than merged at the value\n * level, so the result is always a valid query: `combine` always wraps the two\n * queries in `$or` (broaden — OR has no flat form), while `intersect` merges\n * non-conflicting properties flat and wraps conflicts in `$and` (narrow). The\n * special filters `$select`, `$limit`, `$skip` and `$sort` are merged separately.\n * Inputs are never mutated.\n *\n * This is well suited to merging a client-provided query with a server-side\n * restriction inside a hook.\n *\n * @param target Query to be merged into\n * @param source Query to be merged from\n * @param options\n * @returns the merged query\n *\n * @example\n * ```ts\n * import { mergeQuery } from 'feathers-utils/utils'\n *\n * // combine (default): the two queries always become an $or\n * mergeQuery({ id: 1 }, { id: 2 })\n * // => { $or: [{ id: 1 }, { id: 2 }] }\n *\n * mergeQuery({ status: 'active' }, { authorId: 5 })\n * // => { $or: [{ status: 'active' }, { authorId: 5 }] }\n * ```\n *\n * @example\n * ```ts\n * // intersect: non-conflicting properties merge flat, conflicts become an $and\n * mergeQuery({ status: 'active' }, { authorId: 5 }, { mode: 'intersect' })\n * // => { status: 'active', authorId: 5 }\n *\n * mergeQuery({ id: 1 }, { id: 2 }, { mode: 'intersect' })\n * // => { $and: [{ id: 1 }, { id: 2 }] }\n * ```\n *\n * @see https://utils.feathersjs.com/utils/merge-query.html\n */\nexport function mergeQuery(\n target: Query,\n source: Query,\n options?: MergeQueryOptions,\n): Query {\n const mode = options?.mode ?? 'combine'\n\n const targetFilters = extractQueryFilters(target)\n const sourceFilters = extractQueryFilters(source)\n\n const result: Query = mergeQueryBodies(\n targetFilters.query,\n sourceFilters.query,\n mode,\n )\n\n const $select = mergeSelect(\n targetFilters.$select,\n sourceFilters.$select,\n mode,\n )\n if ($select !== undefined) {\n result.$select = $select\n }\n\n if ('$limit' in sourceFilters) {\n result.$limit = sourceFilters.$limit\n } else if ('$limit' in targetFilters) {\n result.$limit = targetFilters.$limit\n }\n\n if ('$skip' in sourceFilters) {\n result.$skip = sourceFilters.$skip\n } else if ('$skip' in targetFilters) {\n result.$skip = targetFilters.$skip\n }\n\n if ('$sort' in targetFilters || '$sort' in sourceFilters) {\n result.$sort = { ...targetFilters.$sort, ...sourceFilters.$sort }\n }\n\n return result\n}\n","import type { Id, Params } from '@feathersjs/feathers'\nimport type { KeyOf } from '../../internal.utils.js'\n\n/**\n * Deterministic, key-order-independent serialization used to group items with\n * equal patch data in O(1) per item.\n */\nconst stableKey = (value: any): string =>\n JSON.stringify(value, (_key, val) =>\n val && typeof val === 'object' && !Array.isArray(val)\n ? Object.keys(val)\n .sort()\n .reduce<Record<string, any>>((acc, k) => {\n acc[k] = val[k]\n return acc\n }, {})\n : val,\n )\n\nexport type PatchBatchOptions<IdKey extends string> = {\n /** the key of the id property */\n id?: IdKey\n}\n\nexport type PatchBatchResultItem<T = Record<string, unknown>, P = Params> = [\n Id | null,\n T,\n P | undefined,\n]\n\n/**\n * Batch patching utility that takes an array of items to be changed and returns an array of arguments to be called with the `patch` method.\n *\n * This utility is useful when you need to patch multiple items with varying data in as few requests as possible.\n *\n * @example\n * ```ts\n * const items = [\n * { id: 1, value: 10 },\n * { id: 2, value: 10 },\n * { id: 3, value: 20 },\n * ];\n *\n * const batched = patchBatch(items, { id: 'id' });\n * // batched will be:\n * // [\n * // [null, { value: 10 }, { query: { id: { $in: [1, 2] } } }],\n * // [3, { value: 20 }, undefined],\n * // ]\n *\n * await Promise.all(batched.map(args => service.patch(...args)));\n * ```\n *\n * @see https://utils.feathersjs.com/utils/patch-batch.html\n */\nexport function patchBatch<\n T extends Record<string, any>,\n IdKey extends KeyOf<T>,\n P extends Params,\n R extends Omit<T, IdKey> = Omit<T, IdKey>,\n>(\n items: T[],\n options?: PatchBatchOptions<IdKey>,\n): PatchBatchResultItem<R, P>[] {\n const idKey = options?.id ?? 'id'\n\n // group items with identical (id-stripped) data in O(n) via a Map keyed by a\n // stable serialization, instead of an O(n^2) findIndex + deepEqual scan.\n const groups = new Map<string, { ids: Id[]; data: R }>()\n\n for (const item of items) {\n const source = item as Record<string, any>\n const id = source[idKey] as Id\n // shallow copy then drop the id key, so the caller's input is never mutated.\n const data = { ...source }\n delete data[idKey as any]\n\n const key = stableKey(data)\n const existing = groups.get(key)\n\n if (existing) {\n existing.ids.push(id)\n } else {\n groups.set(key, { ids: [id], data: data as unknown as R })\n }\n }\n\n return [...groups.values()].map(({ ids, data }) => {\n return ids.length === 1\n ? ([ids[0], data, undefined] as PatchBatchResultItem<R, P>)\n : ([\n null,\n data,\n {\n query: {\n [idKey]: { $in: ids },\n },\n },\n ] as PatchBatchResultItem<R, P>)\n })\n}\n","import type { Query } from '@feathersjs/feathers'\n\ntype WalkQueryOptionsInit = {\n property?: string\n operator?: string | undefined\n value?: any\n path: (string | number)[]\n}\n\nexport type WalkQueryOptions = {\n property: string\n operator: string | undefined\n value: any\n path: (string | number)[]\n /**\n * Stops the traversal. Any replacement value returned from the current walker\n * call is still applied, but no further properties are visited.\n */\n stop: () => void\n}\n\nexport type WalkQueryCallback = (options: WalkQueryOptions) => any\n\ntype WalkQueryState = { stopped: boolean }\n\nconst _walkQueryUtil = <Q extends Query>(\n query: Q,\n walker: WalkQueryCallback,\n state: WalkQueryState,\n options?: WalkQueryOptionsInit | WalkQueryOptions,\n): Q => {\n const stop = () => {\n state.stopped = true\n }\n\n let cloned = false\n const clonedSecond: Record<string, boolean> = {}\n function set(key: string, value: any, secondKey?: string | number) {\n if (!cloned) {\n query = { ...query }\n cloned = true\n }\n\n if (secondKey !== undefined) {\n if (!clonedSecond[key]) {\n ;(query as any)[key] = { ...query[key] }\n clonedSecond[key] = true\n }\n query[key][secondKey] = value\n return\n }\n\n ;(query as any)[key] = value\n }\n\n for (const key in query) {\n if (state.stopped) {\n break\n }\n\n if (\n (key === '$or' || key === '$and' || key === '$nor') &&\n Array.isArray(query[key])\n ) {\n let array = query[key]\n\n let copiedArray = false\n\n for (let i = 0, n = array.length; i < n; i++) {\n if (state.stopped) {\n break\n }\n\n const nestedQuery = array[i]\n const transformed = _walkQueryUtil(nestedQuery, walker, state, {\n ...options,\n path: [...(options?.path || []), key, i],\n })\n\n if (transformed !== nestedQuery) {\n if (!copiedArray) {\n array = [...array] as any\n copiedArray = true\n }\n\n array[i] = transformed\n }\n }\n\n if (copiedArray) {\n set(key, array)\n }\n } else if (\n typeof query[key] === 'object' &&\n query[key] !== null &&\n !Array.isArray(query[key])\n ) {\n let hasOperator = false\n for (const operator in query[key]) {\n if (state.stopped) {\n break\n }\n\n if (operator.startsWith('$')) {\n hasOperator = true\n const value = walker({\n operator,\n path: [...(options?.path ?? []), key],\n property: key,\n value: query[key][operator],\n stop,\n })\n\n if (value !== undefined && value !== query[key][operator]) {\n set(key, value, operator)\n }\n }\n }\n\n if (!hasOperator) {\n const value = walker({\n operator: undefined,\n path: [...(options?.path ?? []), key],\n property: key,\n value: query[key],\n stop,\n })\n\n if (value !== undefined && value !== query[key]) {\n set(key, value)\n }\n }\n } else {\n const value = walker({\n operator: undefined,\n path: [...(options?.path ?? []), key],\n property: key,\n value: query[key],\n stop,\n })\n\n if (value !== undefined && value !== query[key]) {\n set(key, value)\n }\n }\n }\n\n return query\n}\n\n/**\n * Walks every property of a Feathers query (including nested `$and`/`$or`/`$nor` arrays)\n * and calls the `walker` function for each one. The walker receives the property name, operator,\n * value, path, and a `stop` function, and can return a replacement value. Calling `stop()` halts\n * the traversal early. Returns a new query only if changes were made.\n *\n * @example\n * ```ts\n * import { walkQuery } from 'feathers-utils/utils'\n *\n * const query = walkQuery({ age: { $gt: '18' } }, ({ value, operator }) => {\n * if (operator === '$gt') return Number(value)\n * })\n * // => { age: { $gt: 18 } }\n * ```\n *\n * @example\n * ```ts\n * // stop early once a property is found\n * let found = false\n * walkQuery(query, ({ property, stop }) => {\n * if (property === 'isTemplate') {\n * found = true\n * stop()\n * }\n * })\n * ```\n *\n * @see https://utils.feathersjs.com/utils/walk-query.html\n */\nexport const walkQuery = <Q extends Query>(\n query: Q,\n walker: WalkQueryCallback,\n): Q => {\n return _walkQueryUtil(query, walker, { stopped: false })\n}\n","import type { Query } from '@feathersjs/feathers'\nimport { toArray, type MaybeArray } from '../../internal.utils.js'\nimport { walkQuery } from '../walk-query/walk-query.util.js'\n\n/**\n * Checks whether a Feathers query contains one or more properties — including\n * properties nested inside `$and`/`$or`/`$nor` arrays. Returns `true` as soon as\n * any of the given property names is found. The query is not mutated.\n *\n * @example\n * ```ts\n * import { queryHasProperty } from 'feathers-utils/utils'\n *\n * queryHasProperty({ isTemplate: true }, 'isTemplate') // true\n * queryHasProperty({ $or: [{ isTemplate: true }] }, 'isTemplate') // true\n * queryHasProperty({ age: { $gt: 18 } }, ['isTemplate', 'status']) // false\n * ```\n *\n * @see https://utils.feathersjs.com/utils/query-has-property.html\n */\nexport const queryHasProperty = (\n query: Query,\n property: MaybeArray<string>,\n): boolean => {\n const properties = new Set(toArray(property))\n\n let found = false\n walkQuery(query, ({ property: key, stop }) => {\n if (properties.has(key)) {\n found = true\n stop()\n }\n // returning undefined leaves the value untouched → no mutation\n })\n\n return found\n}\n","import type { Query } from '@feathersjs/feathers'\nimport { addToQuery } from '../add-to-query/add-to-query.util.js'\nimport { queryHasProperty } from '../query-has-property/query-has-property.util.js'\n\n/**\n * Adds default properties to a Feathers query — but only for fields the query does\n * not already constrain. Presence is checked with {@link queryHasProperty}, so a field\n * referenced anywhere (including nested in `$and`/`$or`/`$nor`) is left untouched and\n * the caller keeps control over it. The query is treated as the `data` equivalent of\n * the `defaults` transformer. Each default is applied independently (per-field).\n *\n * @example\n * ```ts\n * import { queryDefaults } from 'feathers-utils/utils'\n *\n * queryDefaults({ status: 'active' }, { isTemplate: false })\n * // => { status: 'active', isTemplate: false }\n *\n * queryDefaults({ $or: [{ isTemplate: true }] }, { isTemplate: false })\n * // => { $or: [{ isTemplate: true }] } (untouched — already referenced)\n * ```\n *\n * @see https://utils.feathersjs.com/utils/query-defaults.html\n */\nexport const queryDefaults = (\n query: Query | undefined,\n defaults: Query,\n): Query => {\n const source: Query = query ?? {}\n\n const toAdd: Query = {}\n for (const key in defaults) {\n if (!queryHasProperty(source, key)) {\n toAdd[key] = defaults[key]\n }\n }\n\n return addToQuery(source, toAdd)\n}\n","import type { HookContext } from '@feathersjs/feathers'\nimport { isMulti, isPaginated } from '../../predicates/index.js'\n\n/**\n * Sets `context.result` to an appropriate empty value based on the hook method.\n * Returns an empty paginated object for paginated `find`, an empty array for multi\n * operations, or `null` for single-item operations. Does nothing if a result already exists.\n *\n * @example\n * ```ts\n * import { skipResult } from 'feathers-utils/utils'\n *\n * // In a before hook to skip the actual database call:\n * skipResult(context)\n * ```\n *\n * @see https://utils.feathersjs.com/utils/skip-result.html\n */\nexport const skipResult = <H extends HookContext = HookContext>(context: H) => {\n if (context.result) {\n return context\n }\n\n const multi = isMulti(context)\n\n if (multi) {\n if (context.method === 'find' && isPaginated(context)) {\n context.result = {\n total: 0,\n skip: 0,\n limit: 0,\n data: [],\n }\n } else {\n context.result = []\n }\n } else {\n context.result = null\n }\n\n return context\n}\n","import type { Paginated } from '@feathersjs/feathers'\n\n/**\n * Ensures a result is in Feathers paginated format (`{ total, limit, skip, data }`).\n * If the input is already paginated, it is returned as-is. If it is a plain array,\n * it is wrapped in a paginated object with `total` and `limit` set to the array length.\n *\n * @example\n * ```ts\n * import { toPaginated } from 'feathers-utils/utils'\n *\n * const paginated = toPaginated([{ id: 1 }, { id: 2 }])\n * // => { total: 2, limit: 2, skip: 0, data: [{ id: 1 }, { id: 2 }] }\n * ```\n *\n * @see https://utils.feathersjs.com/utils/to-paginated.html\n */\nexport function toPaginated<R>(result: R[] | Paginated<R>): Paginated<R> {\n if (Array.isArray(result)) {\n return {\n total: result.length,\n limit: result.length,\n skip: 0,\n data: result,\n }\n }\n return result\n}\n","import type { Params } from '@feathersjs/feathers'\nimport type { TransformParamsFn } from '../../types.js'\n\n/**\n * Safely applies a `transformParams` function to a params object.\n * If no function is provided, the original params are returned unchanged.\n * The function receives a shallow copy of params, so the original is not mutated.\n *\n * @example\n * ```ts\n * import { transformParams } from 'feathers-utils/utils'\n *\n * const params = transformParams(context.params, (p) => { delete p.provider; return p })\n * ```\n *\n * @see https://utils.feathersjs.com/utils/transform-params.html\n */\nexport const transformParams = <P extends Params = Params>(\n params: P,\n fn: TransformParamsFn<P> | undefined,\n): P => {\n if (!fn) {\n return params\n }\n\n const result = fn({ ...params })\n\n return result ?? params\n}\n","import type { Application, HookContext } from '@feathersjs/feathers'\nimport type { KeyOf, NeverFallback } from '../../internal.utils.js'\nimport type { InferFindResultSingle } from '../../utility-types/infer-service-methods.js'\n\n/**\n * The standard Feathers service events, plus any custom event name a service\n * might emit.\n */\nexport type ServiceEventName =\n | 'created'\n | 'updated'\n | 'patched'\n | 'removed'\n | (string & {})\n\nexport type WaitForServiceEventOptions<Result = unknown> = {\n /**\n * Reject after this many milliseconds. Pass `false` to wait indefinitely.\n *\n * @default 5000\n */\n timeout?: number | false\n /**\n * Only resolve for events whose data passes this predicate. Receives the\n * emitted record and the `HookContext` (the second argument Feathers emits).\n */\n filter?: (data: Result, context: HookContext) => boolean\n /**\n * Abort waiting via an `AbortSignal`. The promise rejects with the signal's\n * `reason` (or a generic abort error) and all listeners are detached.\n */\n signal?: AbortSignal\n}\n\n/**\n * Service-agnostic defaults that can be bound once when currying with the app.\n * `filter` is intentionally omitted because it depends on the per-service\n * record type.\n */\nexport type WaitForServiceEventDefaults = Pick<\n WaitForServiceEventOptions,\n 'timeout' | 'signal'\n>\n\nexport type WaitForServiceEventResult<Event extends string, Result> = [\n /** The emitted record. */\n data: Result,\n meta: {\n /** The event that fired (one of the requested events). */\n event: Event\n /** The `HookContext` Feathers emitted alongside the record. */\n context: HookContext\n },\n]\n\n/**\n * Wait for a service event to fire and resolve with the emitted record. Useful\n * in tests to await the result of an asynchronous service event, a bit like\n * `promisify` for Feathers events.\n *\n * Curried: bind the `app` (and optional defaults) once, then call the returned\n * function per service/event. Resolves with a `[data, { event, context }]`\n * tuple: `data` is typed as the service's record type, and `event` is the union\n * of the requested events.\n *\n * Feathers emits events as `emit(event, record, context)` and fires one event\n * per record, so each resolution carries a single record and its `HookContext`.\n *\n * @example\n * ```ts\n * import { waitForServiceEvent } from 'feathers-utils/utils'\n *\n * const app = feathers()\n * const waitForEvent = waitForServiceEvent(app)\n *\n * // Wait for the next `users` record to be created.\n * const [user] = await waitForEvent('users', 'created')\n *\n * // Wait for a specific record, with a custom timeout and filter.\n * const [data, { event }] = await waitForEvent(\n * 'users',\n * ['created', 'patched'],\n * { filter: (user) => user.email === 'jane@example.com', timeout: 1000 },\n * )\n * ```\n *\n * @see https://utils.feathersjs.com/utils/wait-for-service-event.html\n */\nexport function waitForServiceEvent<Services>(\n app: Application<Services>,\n defaultOptions?: WaitForServiceEventDefaults,\n) {\n return function waitForEvent<\n Path extends KeyOf<Services>,\n const Event extends ServiceEventName,\n Service extends Services[Path] = Services[Path],\n Result = NeverFallback<InferFindResultSingle<Service>, unknown>,\n >(\n servicePath: Path,\n eventOrEvents: Event | Event[],\n options?: WaitForServiceEventOptions<Result>,\n ): Promise<WaitForServiceEventResult<Event, Result>> {\n const events = (\n Array.isArray(eventOrEvents) ? eventOrEvents : [eventOrEvents]\n ) as Event[]\n\n const timeout = options?.timeout ?? defaultOptions?.timeout ?? 5000\n const filter = options?.filter\n const signal = options?.signal ?? defaultOptions?.signal\n\n const service = app.service(servicePath)\n\n return new Promise<WaitForServiceEventResult<Event, Result>>(\n (resolve, reject) => {\n let timer: ReturnType<typeof setTimeout> | undefined\n\n // [event, listener] pairs so we know which event fired (Node's\n // EventEmitter does not pass the event name to the listener) and can\n // detach each listener precisely on cleanup.\n const listeners = events.map((event) => {\n const listener = (data: Result, context: HookContext) => {\n if (filter && !filter(data, context)) {\n return\n }\n cleanup()\n resolve([data, { event, context }])\n }\n return [event, listener] as const\n })\n\n const abortError = () =>\n signal?.reason ??\n new Error(\n `Aborted waiting for event \"${events.join(', ')}\" on service \"${String(servicePath)}\"`,\n )\n\n const onAbort = () => {\n cleanup()\n reject(abortError())\n }\n\n function cleanup() {\n if (timer) {\n clearTimeout(timer)\n timer = undefined\n }\n for (const [event, listener] of listeners) {\n ;(service as any).off(event, listener)\n }\n signal?.removeEventListener('abort', onAbort)\n }\n\n if (signal?.aborted) {\n reject(abortError())\n return\n }\n\n if (timeout !== false) {\n timer = setTimeout(() => {\n cleanup()\n reject(\n new Error(\n `Timeout waiting for event \"${events.join(', ')}\" on service \"${String(servicePath)}\"`,\n ),\n )\n }, timeout)\n }\n\n signal?.addEventListener('abort', onAbort, { once: true })\n\n for (const [event, listener] of listeners) {\n ;(service as any).on(event, listener)\n }\n },\n )\n }\n}\n","import type { HookContext } from '@feathersjs/feathers'\nimport { getDataIsArray } from '../get-data-is-array/get-data-is-array.util.js'\nimport { getResultIsArray } from '../get-result-is-array/get-result-is-array.util.js'\nimport { checkContext } from '../check-context/check-context.util.js'\nimport type {\n DataSingleHookContext,\n ResultSingleHookContext,\n} from '../../utility-types/hook-context.js'\n\nexport type ZipDataResultOptions = {\n onMismatch?: (context: HookContext) => void\n}\n\nexport type ZipDataResultItem<D, R> = {\n data: D | undefined\n result: R | undefined\n}\n\n/**\n * Pairs each item in `context.data` with its corresponding item in `context.result` by index.\n * Handles both single-item and array data, normalizing them into an array of `{ data, result }` pairs.\n * Only works in `after`/`around` hooks for `create`, `update`, and `patch` methods.\n *\n * @example\n * ```ts\n * import { zipDataResult } from 'feathers-utils/utils'\n *\n * const pairs = zipDataResult(context)\n * pairs.forEach(({ data, result }) => { /* process each pair *\\/ })\n * ```\n *\n * @see https://utils.feathersjs.com/utils/zip-data-result.html\n */\nexport function zipDataResult<\n H extends HookContext,\n D extends DataSingleHookContext<H> = DataSingleHookContext<H>,\n R extends ResultSingleHookContext<H> = ResultSingleHookContext<H>,\n>(context: H, options?: ZipDataResultOptions): ZipDataResultItem<D, R>[] {\n checkContext(context, ['after', 'around'], ['create', 'update', 'patch'])\n\n const input = getDataIsArray(context)\n const output = getResultIsArray(context)\n\n if (\n input.isArray &&\n output.isArray &&\n input.data.length !== output.result.length\n ) {\n options?.onMismatch?.(context)\n }\n\n const result: ZipDataResultItem<D, R>[] = []\n\n const length = Math.max(input.data.length, output.result.length)\n\n for (let i = 0; i < length; i++) {\n const dataItem = input.isArray ? input.data.at(i) : input.data[0]\n const resultItem = output.result.at(i)\n\n result.push({\n data: dataItem,\n result: resultItem,\n })\n }\n\n return result\n}\n"],"mappings":"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"}