feathers-utils 10.1.0 → 10.2.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.
@@ -1,3 +1,4 @@
1
+ import { r as toArray } from "./internal.utils-BMzV_-xp.mjs";
1
2
  import { d as isPaginated, m as isContext, p as isMulti } from "./predicates-puYa4nkf.mjs";
2
3
  import { a as getResultIsArray, o as getDataIsArray } from "./mutate-result.util-Dqzepn1M.mjs";
3
4
  import { dequal } from "dequal";
@@ -534,7 +535,10 @@ function waitForServiceEvent(app, defaultOptions) {
534
535
  }
535
536
  //#endregion
536
537
  //#region src/utils/walk-query/walk-query.util.ts
537
- const _walkQueryUtil = (query, walker, options) => {
538
+ const _walkQueryUtil = (query, walker, state, options) => {
539
+ const stop = () => {
540
+ state.stopped = true;
541
+ };
538
542
  let cloned = false;
539
543
  const clonedSecond = {};
540
544
  function set(key, value, secondKey) {
@@ -552,64 +556,75 @@ const _walkQueryUtil = (query, walker, options) => {
552
556
  }
553
557
  query[key] = value;
554
558
  }
555
- for (const key in query) if ((key === "$or" || key === "$and" || key === "$nor") && Array.isArray(query[key])) {
556
- let array = query[key];
557
- let copiedArray = false;
558
- for (let i = 0, n = array.length; i < n; i++) {
559
- const nestedQuery = array[i];
560
- const transformed = _walkQueryUtil(nestedQuery, walker, {
561
- ...options,
562
- path: [
563
- ...options?.path || [],
564
- key,
565
- i
566
- ]
567
- });
568
- if (transformed !== nestedQuery) {
569
- if (!copiedArray) {
570
- array = [...array];
571
- copiedArray = true;
559
+ for (const key in query) {
560
+ if (state.stopped) break;
561
+ if ((key === "$or" || key === "$and" || key === "$nor") && Array.isArray(query[key])) {
562
+ let array = query[key];
563
+ let copiedArray = false;
564
+ for (let i = 0, n = array.length; i < n; i++) {
565
+ if (state.stopped) break;
566
+ const nestedQuery = array[i];
567
+ const transformed = _walkQueryUtil(nestedQuery, walker, state, {
568
+ ...options,
569
+ path: [
570
+ ...options?.path || [],
571
+ key,
572
+ i
573
+ ]
574
+ });
575
+ if (transformed !== nestedQuery) {
576
+ if (!copiedArray) {
577
+ array = [...array];
578
+ copiedArray = true;
579
+ }
580
+ array[i] = transformed;
572
581
  }
573
- array[i] = transformed;
574
582
  }
575
- }
576
- if (copiedArray) set(key, array);
577
- } else if (typeof query[key] === "object" && query[key] !== null && !Array.isArray(query[key])) {
578
- let hasOperator = false;
579
- for (const operator in query[key]) if (operator.startsWith("$")) {
580
- hasOperator = true;
581
- const value = walker({
582
- operator,
583
- path: [...options?.path ?? [], key],
584
- property: key,
585
- value: query[key][operator]
586
- });
587
- if (value !== void 0 && value !== query[key][operator]) set(key, value, operator);
588
- }
589
- if (!hasOperator) {
583
+ if (copiedArray) set(key, array);
584
+ } else if (typeof query[key] === "object" && query[key] !== null && !Array.isArray(query[key])) {
585
+ let hasOperator = false;
586
+ for (const operator in query[key]) {
587
+ if (state.stopped) break;
588
+ if (operator.startsWith("$")) {
589
+ hasOperator = true;
590
+ const value = walker({
591
+ operator,
592
+ path: [...options?.path ?? [], key],
593
+ property: key,
594
+ value: query[key][operator],
595
+ stop
596
+ });
597
+ if (value !== void 0 && value !== query[key][operator]) set(key, value, operator);
598
+ }
599
+ }
600
+ if (!hasOperator) {
601
+ const value = walker({
602
+ operator: void 0,
603
+ path: [...options?.path ?? [], key],
604
+ property: key,
605
+ value: query[key],
606
+ stop
607
+ });
608
+ if (value !== void 0 && value !== query[key]) set(key, value);
609
+ }
610
+ } else {
590
611
  const value = walker({
591
612
  operator: void 0,
592
613
  path: [...options?.path ?? [], key],
593
614
  property: key,
594
- value: query[key]
615
+ value: query[key],
616
+ stop
595
617
  });
596
618
  if (value !== void 0 && value !== query[key]) set(key, value);
597
619
  }
598
- } else {
599
- const value = walker({
600
- operator: void 0,
601
- path: [...options?.path ?? [], key],
602
- property: key,
603
- value: query[key]
604
- });
605
- if (value !== void 0 && value !== query[key]) set(key, value);
606
620
  }
607
621
  return query;
608
622
  };
609
623
  /**
610
624
  * Walks every property of a Feathers query (including nested `$and`/`$or`/`$nor` arrays)
611
625
  * and calls the `walker` function for each one. The walker receives the property name, operator,
612
- * value, and path, and can return a replacement value. Returns a new query only if changes were made.
626
+ * value, path, and a `stop` function, and can return a replacement value. Calling `stop()` halts
627
+ * the traversal early. Returns a new query only if changes were made.
613
628
  *
614
629
  * @example
615
630
  * ```ts
@@ -621,10 +636,79 @@ const _walkQueryUtil = (query, walker, options) => {
621
636
  * // => { age: { $gt: 18 } }
622
637
  * ```
623
638
  *
639
+ * @example
640
+ * ```ts
641
+ * // stop early once a property is found
642
+ * let found = false
643
+ * walkQuery(query, ({ property, stop }) => {
644
+ * if (property === 'isTemplate') {
645
+ * found = true
646
+ * stop()
647
+ * }
648
+ * })
649
+ * ```
650
+ *
624
651
  * @see https://utils.feathersjs.com/utils/walk-query.html
625
652
  */
626
653
  const walkQuery = (query, walker) => {
627
- return _walkQueryUtil(query, walker);
654
+ return _walkQueryUtil(query, walker, { stopped: false });
655
+ };
656
+ //#endregion
657
+ //#region src/utils/query-has-property/query-has-property.util.ts
658
+ /**
659
+ * Checks whether a Feathers query contains one or more properties — including
660
+ * properties nested inside `$and`/`$or`/`$nor` arrays. Returns `true` as soon as
661
+ * any of the given property names is found. The query is not mutated.
662
+ *
663
+ * @example
664
+ * ```ts
665
+ * import { queryHasProperty } from 'feathers-utils/utils'
666
+ *
667
+ * queryHasProperty({ isTemplate: true }, 'isTemplate') // true
668
+ * queryHasProperty({ $or: [{ isTemplate: true }] }, 'isTemplate') // true
669
+ * queryHasProperty({ age: { $gt: 18 } }, ['isTemplate', 'status']) // false
670
+ * ```
671
+ *
672
+ * @see https://utils.feathersjs.com/utils/query-has-property.html
673
+ */
674
+ const queryHasProperty = (query, property) => {
675
+ const properties = new Set(toArray(property));
676
+ let found = false;
677
+ walkQuery(query, ({ property: key, stop }) => {
678
+ if (properties.has(key)) {
679
+ found = true;
680
+ stop();
681
+ }
682
+ });
683
+ return found;
684
+ };
685
+ //#endregion
686
+ //#region src/utils/query-defaults/query-defaults.util.ts
687
+ /**
688
+ * Adds default properties to a Feathers query — but only for fields the query does
689
+ * not already constrain. Presence is checked with {@link queryHasProperty}, so a field
690
+ * referenced anywhere (including nested in `$and`/`$or`/`$nor`) is left untouched and
691
+ * the caller keeps control over it. The query is treated as the `data` equivalent of
692
+ * the `defaults` transformer. Each default is applied independently (per-field).
693
+ *
694
+ * @example
695
+ * ```ts
696
+ * import { queryDefaults } from 'feathers-utils/utils'
697
+ *
698
+ * queryDefaults({ status: 'active' }, { isTemplate: false })
699
+ * // => { status: 'active', isTemplate: false }
700
+ *
701
+ * queryDefaults({ $or: [{ isTemplate: true }] }, { isTemplate: false })
702
+ * // => { $or: [{ isTemplate: true }] } (untouched — already referenced)
703
+ * ```
704
+ *
705
+ * @see https://utils.feathersjs.com/utils/query-defaults.html
706
+ */
707
+ const queryDefaults = (query, defaults) => {
708
+ const source = query ?? {};
709
+ const toAdd = {};
710
+ for (const key in defaults) if (!queryHasProperty(source, key)) toAdd[key] = defaults[key];
711
+ return addToQuery(source, toAdd);
628
712
  };
629
713
  //#endregion
630
714
  //#region src/utils/zip-data-result/zip-data-result.util.ts
@@ -665,6 +749,6 @@ function zipDataResult(context, options) {
665
749
  return result;
666
750
  }
667
751
  //#endregion
668
- export { toPaginated as a, iterateFind as c, contextToJson as d, checkContext as f, sortQueryProperties as g, addSkip as h, transformParams as i, getExposedMethods as l, chunkFind as m, walkQuery as n, skipResult as o, addToQuery as p, waitForServiceEvent as r, patchBatch as s, zipDataResult as t, defineHooks as u };
752
+ export { addSkip as _, waitForServiceEvent as a, skipResult as c, getExposedMethods as d, defineHooks as f, chunkFind as g, addToQuery as h, walkQuery as i, patchBatch as l, checkContext as m, queryDefaults as n, transformParams as o, contextToJson as p, queryHasProperty as r, toPaginated as s, zipDataResult as t, iterateFind as u, sortQueryProperties as v };
669
753
 
670
- //# sourceMappingURL=utils-Br6DNQ1B.mjs.map
754
+ //# sourceMappingURL=utils-BAIcSl7u.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-BAIcSl7u.mjs","names":["deepEqual"],"sources":["../src/utils/sort-query-properties/sort-query-properties.util.ts","../src/utils/add-skip/add-skip.util.ts","../src/utils/chunk-find/chunk-find.util.ts","../src/utils/add-to-query/add-to-query.util.ts","../src/utils/check-context/check-context.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/patch-batch/patch-batch.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/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/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 { 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 { 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.\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 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 { 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 { 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 { 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 { 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 { 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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"}
package/dist/utils.d.mts CHANGED
@@ -1,3 +1,3 @@
1
- import { A as defineHooks, C as GetResultIsArrayOptions, D as getExposedMethods, E as getPaginate, I as addToQuery, L as chunkFind, M as CheckContextOptions, N as checkContext, O as GetDataIsArrayReturn, R as SkipHookName, S as iterateFind, T as getResultIsArray, _ as replaceResult, a as WalkQueryOptions, b as PatchBatchResultItem, c as WaitForServiceEventDefaults, d as waitForServiceEvent, f as sortQueryProperties, g as ReplaceResultOptions, h as skipResult, i as WalkQueryCallback, j as contextToJson, k as getDataIsArray, l as WaitForServiceEventOptions, m as toPaginated, n as ZipDataResultOptions, o as walkQuery, p as transformParams, r as zipDataResult, s as ServiceEventName, t as ZipDataResultItem, u as WaitForServiceEventResult, v as replaceData, w as GetResultIsArrayReturn, x as patchBatch, y as PatchBatchOptions, z as addSkip } from "./index-DKA0E_ad.mjs";
1
+ import { A as GetDataIsArrayReturn, B as SkipHookName, C as patchBatch, D as getResultIsArray, E as GetResultIsArrayReturn, F as checkContext, M as defineHooks, N as contextToJson, O as getPaginate, P as CheckContextOptions, R as addToQuery, S as PatchBatchResultItem, T as GetResultIsArrayOptions, V as addSkip, _ as skipResult, a as queryHasProperty, b as replaceData, c as walkQuery, d as WaitForServiceEventOptions, f as WaitForServiceEventResult, g as toPaginated, h as transformParams, i as queryDefaults, j as getDataIsArray, k as getExposedMethods, l as ServiceEventName, m as sortQueryProperties, n as ZipDataResultOptions, o as WalkQueryCallback, p as waitForServiceEvent, r as zipDataResult, s as WalkQueryOptions, t as ZipDataResultItem, u as WaitForServiceEventDefaults, v as ReplaceResultOptions, w as iterateFind, x as PatchBatchOptions, y as replaceResult, z as chunkFind } from "./index-CKAzIogj.mjs";
2
2
  import { n as mutateResult, r as mutateData, t as MutateResultOptions } from "./mutate-result.util-DR9JMsvl.mjs";
3
- export { CheckContextOptions, GetDataIsArrayReturn, GetResultIsArrayOptions, GetResultIsArrayReturn, MutateResultOptions, PatchBatchOptions, PatchBatchResultItem, ReplaceResultOptions, ServiceEventName, SkipHookName, WaitForServiceEventDefaults, WaitForServiceEventOptions, WaitForServiceEventResult, WalkQueryCallback, WalkQueryOptions, ZipDataResultItem, ZipDataResultOptions, addSkip, addToQuery, checkContext, chunkFind, contextToJson, defineHooks, getDataIsArray, getExposedMethods, getPaginate, getResultIsArray, iterateFind, mutateData, mutateResult, patchBatch, replaceData, replaceResult, skipResult, sortQueryProperties, toPaginated, transformParams, waitForServiceEvent, walkQuery, zipDataResult };
3
+ export { CheckContextOptions, GetDataIsArrayReturn, GetResultIsArrayOptions, GetResultIsArrayReturn, MutateResultOptions, PatchBatchOptions, PatchBatchResultItem, ReplaceResultOptions, ServiceEventName, SkipHookName, WaitForServiceEventDefaults, WaitForServiceEventOptions, WaitForServiceEventResult, WalkQueryCallback, WalkQueryOptions, ZipDataResultItem, ZipDataResultOptions, addSkip, addToQuery, checkContext, chunkFind, contextToJson, defineHooks, getDataIsArray, getExposedMethods, getPaginate, getResultIsArray, iterateFind, mutateData, mutateResult, patchBatch, queryDefaults, queryHasProperty, replaceData, replaceResult, skipResult, sortQueryProperties, toPaginated, transformParams, waitForServiceEvent, walkQuery, zipDataResult };
package/dist/utils.mjs CHANGED
@@ -1,4 +1,4 @@
1
1
  import { f as getPaginate } from "./predicates-puYa4nkf.mjs";
2
- import { a as toPaginated, c as iterateFind, d as contextToJson, f as checkContext, g as sortQueryProperties, h as addSkip, i as transformParams, l as getExposedMethods, m as chunkFind, n as walkQuery, o as skipResult, p as addToQuery, r as waitForServiceEvent, s as patchBatch, t as zipDataResult, u as defineHooks } from "./utils-Br6DNQ1B.mjs";
2
+ import { _ as addSkip, a as waitForServiceEvent, c as skipResult, d as getExposedMethods, f as defineHooks, g as chunkFind, h as addToQuery, i as walkQuery, l as patchBatch, m as checkContext, n as queryDefaults, o as transformParams, p as contextToJson, r as queryHasProperty, s as toPaginated, t as zipDataResult, u as iterateFind, v as sortQueryProperties } from "./utils-BAIcSl7u.mjs";
3
3
  import { a as getResultIsArray, i as replaceData, n as replaceResult, o as getDataIsArray, r as mutateData, t as mutateResult } from "./mutate-result.util-Dqzepn1M.mjs";
4
- export { addSkip, addToQuery, checkContext, chunkFind, contextToJson, defineHooks, getDataIsArray, getExposedMethods, getPaginate, getResultIsArray, iterateFind, mutateData, mutateResult, patchBatch, replaceData, replaceResult, skipResult, sortQueryProperties, toPaginated, transformParams, waitForServiceEvent, walkQuery, zipDataResult };
4
+ export { addSkip, addToQuery, checkContext, chunkFind, contextToJson, defineHooks, getDataIsArray, getExposedMethods, getPaginate, getResultIsArray, iterateFind, mutateData, mutateResult, patchBatch, queryDefaults, queryHasProperty, replaceData, replaceResult, skipResult, sortQueryProperties, toPaginated, transformParams, waitForServiceEvent, walkQuery, zipDataResult };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feathers-utils",
3
- "version": "10.1.0",
3
+ "version": "10.2.0",
4
4
  "description": "Useful hooks and utils for use with feathers services.",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",
@@ -9,6 +9,7 @@ export * from './disable-pagination/disable-pagination.hook.js'
9
9
  export * from './disallow/disallow.hook.js'
10
10
  export * from './iff-else/iff-else.hook.js'
11
11
  export * from './iff/iff.hook.js'
12
+ export * from './mute-event/mute-event.hook.js'
12
13
  export * from './on-delete/on-delete.hook.js'
13
14
  export * from './params-for-server/params-for-server.hook.js'
14
15
  export * from './params-from-client/params-from-client.hook.js'
@@ -16,6 +17,7 @@ export * from './prevent-changes/prevent-changes.hook.js'
16
17
  export * from './rate-limit/rate-limit.hook.js'
17
18
  export * from './set-data/set-data.hook.js'
18
19
  export * from './set-field/set-field.hook.js'
20
+ export * from './set-query-defaults/set-query-defaults.hook.js'
19
21
  export * from './set-result/set-result.hook.js'
20
22
  export * from './set-slug/set-slug.hook.js'
21
23
  export * from './skippable/skippable.hook.js'
@@ -0,0 +1,56 @@
1
+ import type { HookContext, NextFunction } from '@feathersjs/feathers'
2
+ import type { PredicateFn } from '../../types.js'
3
+
4
+ export type MuteEventOptions<H extends HookContext = HookContext> = {
5
+ /**
6
+ * Only mute when this is truthy. Can be a boolean or a predicate that
7
+ * receives the `HookContext`. Defaults to always muting.
8
+ *
9
+ * @example isProvider('server')
10
+ */
11
+ when?: boolean | PredicateFn<H>
12
+ }
13
+
14
+ /**
15
+ * Suppresses the service event for the current call by setting `context.event`
16
+ * to `null`. Feathers emits the standard `created`/`updated`/`patched`/`removed`
17
+ * event (the value of `context.event`) after the method runs; setting it to
18
+ * `null` prevents that emission so real-time subscribers and channels are not
19
+ * notified.
20
+ *
21
+ * Useful for seeding, migrations and internal syncs that should not trigger
22
+ * downstream listeners. Works as a `before`, `after` or `around` hook.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import { muteEvent } from 'feathers-utils/hooks'
27
+ * import { isProvider } from 'feathers-utils/predicates'
28
+ *
29
+ * app.service('users').hooks({
30
+ * before: {
31
+ * all: [muteEvent()], // mute every call
32
+ * create: [muteEvent({ when: isProvider('server') })], // only server calls
33
+ * }
34
+ * })
35
+ * ```
36
+ *
37
+ * @see https://utils.feathersjs.com/hooks/mute-event.html
38
+ */
39
+ export const muteEvent = <H extends HookContext = HookContext>(
40
+ options?: MuteEventOptions<H>,
41
+ ) => {
42
+ const when = options?.when
43
+
44
+ return async (context: H, next?: NextFunction): Promise<void> => {
45
+ const should =
46
+ typeof when === 'function' ? await when(context) : (when ?? true)
47
+
48
+ if (should) {
49
+ context.event = null
50
+ }
51
+
52
+ if (next) {
53
+ await next()
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,37 @@
1
+ import type { HookContext, NextFunction, Query } from '@feathersjs/feathers'
2
+ import { queryDefaults } from '../../utils/query-defaults/query-defaults.util.js'
3
+
4
+ /**
5
+ * Adds default properties to `context.params.query` for fields the incoming query does
6
+ * not already constrain (including fields referenced nested in `$and`/`$or`/`$nor`).
7
+ * The query equivalent of the `defaults` transformer: e.g. hide template rows by default
8
+ * while still letting callers opt in via `{ isTemplate: true }`. This is the same pattern
9
+ * `softDelete` uses to filter out deleted rows. Works as a `before` or `around` hook.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { setQueryDefaults } from 'feathers-utils/hooks'
14
+ *
15
+ * app.service('posts').hooks({
16
+ * before: { all: [setQueryDefaults({ isTemplate: false })] },
17
+ * })
18
+ * // find() => filters out templates
19
+ * // find({ query: { isTemplate: true } }) => caller keeps control
20
+ * ```
21
+ *
22
+ * @see https://utils.feathersjs.com/hooks/set-query-defaults.html
23
+ */
24
+ export const setQueryDefaults = <H extends HookContext = HookContext>(
25
+ defaults: Query,
26
+ ) => {
27
+ function hook(context: H): void
28
+ function hook(context: H, next: NextFunction): Promise<void>
29
+ function hook(context: H, next?: NextFunction): void | Promise<void> {
30
+ context.params.query = queryDefaults(context.params.query, defaults)
31
+
32
+ if (next) return next()
33
+
34
+ return
35
+ }
36
+ return hook
37
+ }
@@ -1,5 +1,5 @@
1
1
  import type { HookContext, NextFunction } from '@feathersjs/feathers'
2
- import { addToQuery, checkContext } from '../../utils/index.js'
2
+ import { addToQuery, checkContext, queryDefaults } from '../../utils/index.js'
3
3
  import type { TransformParamsFn } from '../../types.js'
4
4
  import { transformParams } from '../../utils/transform-params/transform-params.util.js'
5
5
  import { early, type Promisable } from '../../internal.utils.js'
@@ -36,6 +36,18 @@ export interface SoftDeleteOptions<H extends HookContext = HookContext> {
36
36
  * If you set this option to `true`, it will use the `.patch()` method with hooks instead.
37
37
  */
38
38
  usePatchWithHooks?: boolean
39
+
40
+ /**
41
+ * By default, if the incoming `params.query` already references a key of
42
+ * `deletedQuery` (e.g. `deletedAt`) — including nested inside `$and`/`$or`/`$nor` —
43
+ * the `deletedQuery` filter is NOT added, letting the caller read soft-deleted
44
+ * items while `remove` still soft-deletes them.
45
+ *
46
+ * Set this to `false` to always enforce the `deletedQuery` filter.
47
+ *
48
+ * @default true
49
+ */
50
+ allowQueryOverride?: boolean
39
51
  }
40
52
 
41
53
  /**
@@ -75,14 +87,16 @@ export const softDelete = <H extends HookContext = HookContext>(
75
87
  return
76
88
  }
77
89
 
78
- const { deletedQuery, removeData } = options
90
+ const { deletedQuery, removeData, allowQueryOverride = true } = options
79
91
 
80
92
  let deleteQuery = getValue(deletedQuery, context)
81
93
  if (isPromise(deleteQuery)) {
82
94
  deleteQuery = await deleteQuery
83
95
  }
84
96
 
85
- const query = addToQuery(context.params.query, deleteQuery)
97
+ const query = allowQueryOverride
98
+ ? queryDefaults(context.params.query, deleteQuery)
99
+ : addToQuery(context.params.query, deleteQuery)
86
100
 
87
101
  const params = transformParams(
88
102
  {
@@ -20,4 +20,6 @@ export * from './transform-params/transform-params.util.js'
20
20
  export * from './sort-query-properties/sort-query-properties.util.js'
21
21
  export * from './wait-for-service-event/wait-for-service-event.util.js'
22
22
  export * from './walk-query/walk-query.util.js'
23
+ export * from './query-has-property/query-has-property.util.js'
24
+ export * from './query-defaults/query-defaults.util.js'
23
25
  export * from './zip-data-result/zip-data-result.util.js'
@@ -0,0 +1,39 @@
1
+ import type { Query } from '@feathersjs/feathers'
2
+ import { addToQuery } from '../add-to-query/add-to-query.util.js'
3
+ import { queryHasProperty } from '../query-has-property/query-has-property.util.js'
4
+
5
+ /**
6
+ * Adds default properties to a Feathers query — but only for fields the query does
7
+ * not already constrain. Presence is checked with {@link queryHasProperty}, so a field
8
+ * referenced anywhere (including nested in `$and`/`$or`/`$nor`) is left untouched and
9
+ * the caller keeps control over it. The query is treated as the `data` equivalent of
10
+ * the `defaults` transformer. Each default is applied independently (per-field).
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * import { queryDefaults } from 'feathers-utils/utils'
15
+ *
16
+ * queryDefaults({ status: 'active' }, { isTemplate: false })
17
+ * // => { status: 'active', isTemplate: false }
18
+ *
19
+ * queryDefaults({ $or: [{ isTemplate: true }] }, { isTemplate: false })
20
+ * // => { $or: [{ isTemplate: true }] } (untouched — already referenced)
21
+ * ```
22
+ *
23
+ * @see https://utils.feathersjs.com/utils/query-defaults.html
24
+ */
25
+ export const queryDefaults = (
26
+ query: Query | undefined,
27
+ defaults: Query,
28
+ ): Query => {
29
+ const source: Query = query ?? {}
30
+
31
+ const toAdd: Query = {}
32
+ for (const key in defaults) {
33
+ if (!queryHasProperty(source, key)) {
34
+ toAdd[key] = defaults[key]
35
+ }
36
+ }
37
+
38
+ return addToQuery(source, toAdd)
39
+ }
@@ -0,0 +1,37 @@
1
+ import type { Query } from '@feathersjs/feathers'
2
+ import { toArray, type MaybeArray } from '../../internal.utils.js'
3
+ import { walkQuery } from '../walk-query/walk-query.util.js'
4
+
5
+ /**
6
+ * Checks whether a Feathers query contains one or more properties — including
7
+ * properties nested inside `$and`/`$or`/`$nor` arrays. Returns `true` as soon as
8
+ * any of the given property names is found. The query is not mutated.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { queryHasProperty } from 'feathers-utils/utils'
13
+ *
14
+ * queryHasProperty({ isTemplate: true }, 'isTemplate') // true
15
+ * queryHasProperty({ $or: [{ isTemplate: true }] }, 'isTemplate') // true
16
+ * queryHasProperty({ age: { $gt: 18 } }, ['isTemplate', 'status']) // false
17
+ * ```
18
+ *
19
+ * @see https://utils.feathersjs.com/utils/query-has-property.html
20
+ */
21
+ export const queryHasProperty = (
22
+ query: Query,
23
+ property: MaybeArray<string>,
24
+ ): boolean => {
25
+ const properties = new Set(toArray(property))
26
+
27
+ let found = false
28
+ walkQuery(query, ({ property: key, stop }) => {
29
+ if (properties.has(key)) {
30
+ found = true
31
+ stop()
32
+ }
33
+ // returning undefined leaves the value untouched → no mutation
34
+ })
35
+
36
+ return found
37
+ }