feathers-utils 10.0.4 → 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,4 +1,4 @@
1
- import { E as KeyOf, O as MaybeArray, f as HookType$1, j as UnpackMaybeArray, l as DispatchOption, n as ResultSingleHookContext, p as MethodName, t as DataSingleHookContext, y as TransformParamsFn } from "./hook-context-BsxU1vfN.mjs";
1
+ import { E as KeyOf, O as MaybeArray, f as HookType$1, j as UnpackMaybeArray, k as NeverFallback, l as DispatchOption, n as ResultSingleHookContext, p as MethodName, t as DataSingleHookContext, y as TransformParamsFn } from "./hook-context-BsxU1vfN.mjs";
2
2
  import { d as InferFindParams, m as InferFindResultSingle } from "./infer-service-methods-C-9x7P40.mjs";
3
3
  import { Application, HookContext, HookOptions, HookType, Id, Paginated, Params, Query, Service } from "@feathersjs/feathers";
4
4
  import { PaginationOptions as PaginationOptions$1 } from "@feathersjs/adapter-commons";
@@ -464,18 +464,93 @@ declare const transformParams: <P extends Params = Params>(params: P, fn: Transf
464
464
  */
465
465
  declare const sortQueryProperties: <Q extends Query>(query: Q) => Q;
466
466
  //#endregion
467
+ //#region src/utils/wait-for-service-event/wait-for-service-event.util.d.ts
468
+ /**
469
+ * The standard Feathers service events, plus any custom event name a service
470
+ * might emit.
471
+ */
472
+ type ServiceEventName = 'created' | 'updated' | 'patched' | 'removed' | (string & {});
473
+ type WaitForServiceEventOptions<Result = unknown> = {
474
+ /**
475
+ * Reject after this many milliseconds. Pass `false` to wait indefinitely.
476
+ *
477
+ * @default 5000
478
+ */
479
+ timeout?: number | false;
480
+ /**
481
+ * Only resolve for events whose data passes this predicate. Receives the
482
+ * emitted record and the `HookContext` (the second argument Feathers emits).
483
+ */
484
+ filter?: (data: Result, context: HookContext) => boolean;
485
+ /**
486
+ * Abort waiting via an `AbortSignal`. The promise rejects with the signal's
487
+ * `reason` (or a generic abort error) and all listeners are detached.
488
+ */
489
+ signal?: AbortSignal;
490
+ };
491
+ /**
492
+ * Service-agnostic defaults that can be bound once when currying with the app.
493
+ * `filter` is intentionally omitted because it depends on the per-service
494
+ * record type.
495
+ */
496
+ type WaitForServiceEventDefaults = Pick<WaitForServiceEventOptions, 'timeout' | 'signal'>;
497
+ type WaitForServiceEventResult<Event extends string, Result> = [/** The emitted record. */data: Result, meta: {
498
+ /** The event that fired (one of the requested events). */event: Event; /** The `HookContext` Feathers emitted alongside the record. */
499
+ context: HookContext;
500
+ }];
501
+ /**
502
+ * Wait for a service event to fire and resolve with the emitted record. Useful
503
+ * in tests to await the result of an asynchronous service event, a bit like
504
+ * `promisify` for Feathers events.
505
+ *
506
+ * Curried: bind the `app` (and optional defaults) once, then call the returned
507
+ * function per service/event. Resolves with a `[data, { event, context }]`
508
+ * tuple: `data` is typed as the service's record type, and `event` is the union
509
+ * of the requested events.
510
+ *
511
+ * Feathers emits events as `emit(event, record, context)` and fires one event
512
+ * per record, so each resolution carries a single record and its `HookContext`.
513
+ *
514
+ * @example
515
+ * ```ts
516
+ * import { waitForServiceEvent } from 'feathers-utils/utils'
517
+ *
518
+ * const app = feathers()
519
+ * const waitForEvent = waitForServiceEvent(app)
520
+ *
521
+ * // Wait for the next `users` record to be created.
522
+ * const [user] = await waitForEvent('users', 'created')
523
+ *
524
+ * // Wait for a specific record, with a custom timeout and filter.
525
+ * const [data, { event }] = await waitForEvent(
526
+ * 'users',
527
+ * ['created', 'patched'],
528
+ * { filter: (user) => user.email === 'jane@example.com', timeout: 1000 },
529
+ * )
530
+ * ```
531
+ *
532
+ * @see https://utils.feathersjs.com/utils/wait-for-service-event.html
533
+ */
534
+ declare function waitForServiceEvent<Services>(app: Application<Services>, defaultOptions?: WaitForServiceEventDefaults): <Path extends KeyOf<Services>, const Event extends ServiceEventName, Service extends Services[Path] = Services[Path], Result = NeverFallback<InferFindResultSingle<Service>, unknown>>(servicePath: Path, eventOrEvents: Event | Event[], options?: WaitForServiceEventOptions<Result>) => Promise<WaitForServiceEventResult<Event, Result>>;
535
+ //#endregion
467
536
  //#region src/utils/walk-query/walk-query.util.d.ts
468
537
  type WalkQueryOptions = {
469
538
  property: string;
470
539
  operator: string | undefined;
471
540
  value: any;
472
541
  path: (string | number)[];
542
+ /**
543
+ * Stops the traversal. Any replacement value returned from the current walker
544
+ * call is still applied, but no further properties are visited.
545
+ */
546
+ stop: () => void;
473
547
  };
474
548
  type WalkQueryCallback = (options: WalkQueryOptions) => any;
475
549
  /**
476
550
  * Walks every property of a Feathers query (including nested `$and`/`$or`/`$nor` arrays)
477
551
  * and calls the `walker` function for each one. The walker receives the property name, operator,
478
- * value, and path, and can return a replacement value. Returns a new query only if changes were made.
552
+ * value, path, and a `stop` function, and can return a replacement value. Calling `stop()` halts
553
+ * the traversal early. Returns a new query only if changes were made.
479
554
  *
480
555
  * @example
481
556
  * ```ts
@@ -487,10 +562,64 @@ type WalkQueryCallback = (options: WalkQueryOptions) => any;
487
562
  * // => { age: { $gt: 18 } }
488
563
  * ```
489
564
  *
565
+ * @example
566
+ * ```ts
567
+ * // stop early once a property is found
568
+ * let found = false
569
+ * walkQuery(query, ({ property, stop }) => {
570
+ * if (property === 'isTemplate') {
571
+ * found = true
572
+ * stop()
573
+ * }
574
+ * })
575
+ * ```
576
+ *
490
577
  * @see https://utils.feathersjs.com/utils/walk-query.html
491
578
  */
492
579
  declare const walkQuery: <Q extends Query>(query: Q, walker: WalkQueryCallback) => Q;
493
580
  //#endregion
581
+ //#region src/utils/query-has-property/query-has-property.util.d.ts
582
+ /**
583
+ * Checks whether a Feathers query contains one or more properties — including
584
+ * properties nested inside `$and`/`$or`/`$nor` arrays. Returns `true` as soon as
585
+ * any of the given property names is found. The query is not mutated.
586
+ *
587
+ * @example
588
+ * ```ts
589
+ * import { queryHasProperty } from 'feathers-utils/utils'
590
+ *
591
+ * queryHasProperty({ isTemplate: true }, 'isTemplate') // true
592
+ * queryHasProperty({ $or: [{ isTemplate: true }] }, 'isTemplate') // true
593
+ * queryHasProperty({ age: { $gt: 18 } }, ['isTemplate', 'status']) // false
594
+ * ```
595
+ *
596
+ * @see https://utils.feathersjs.com/utils/query-has-property.html
597
+ */
598
+ declare const queryHasProperty: (query: Query, property: MaybeArray<string>) => boolean;
599
+ //#endregion
600
+ //#region src/utils/query-defaults/query-defaults.util.d.ts
601
+ /**
602
+ * Adds default properties to a Feathers query — but only for fields the query does
603
+ * not already constrain. Presence is checked with {@link queryHasProperty}, so a field
604
+ * referenced anywhere (including nested in `$and`/`$or`/`$nor`) is left untouched and
605
+ * the caller keeps control over it. The query is treated as the `data` equivalent of
606
+ * the `defaults` transformer. Each default is applied independently (per-field).
607
+ *
608
+ * @example
609
+ * ```ts
610
+ * import { queryDefaults } from 'feathers-utils/utils'
611
+ *
612
+ * queryDefaults({ status: 'active' }, { isTemplate: false })
613
+ * // => { status: 'active', isTemplate: false }
614
+ *
615
+ * queryDefaults({ $or: [{ isTemplate: true }] }, { isTemplate: false })
616
+ * // => { $or: [{ isTemplate: true }] } (untouched — already referenced)
617
+ * ```
618
+ *
619
+ * @see https://utils.feathersjs.com/utils/query-defaults.html
620
+ */
621
+ declare const queryDefaults: (query: Query | undefined, defaults: Query) => Query;
622
+ //#endregion
494
623
  //#region src/utils/zip-data-result/zip-data-result.util.d.ts
495
624
  type ZipDataResultOptions = {
496
625
  onMismatch?: (context: HookContext) => void;
@@ -516,5 +645,5 @@ type ZipDataResultItem<D, R> = {
516
645
  */
517
646
  declare function zipDataResult<H extends HookContext, D extends DataSingleHookContext<H> = DataSingleHookContext<H>, R extends ResultSingleHookContext<H> = ResultSingleHookContext<H>>(context: H, options?: ZipDataResultOptions): ZipDataResultItem<D, R>[];
518
647
  //#endregion
519
- export { isContext as A, GetDataIsArrayReturn as C, CheckContextOptions as D, contextToJson as E, chunkFind as M, SkipHookName as N, checkContext as O, addSkip as P, getExposedMethods as S, defineHooks as T, iterateFind as _, WalkQueryOptions as a, getResultIsArray as b, transformParams as c, ReplaceResultOptions as d, replaceResult as f, patchBatch as g, PatchBatchResultItem as h, WalkQueryCallback as i, addToQuery as j, IsContextOptions as k, toPaginated as l, PatchBatchOptions as m, ZipDataResultOptions as n, walkQuery as o, replaceData as p, zipDataResult as r, sortQueryProperties as s, ZipDataResultItem as t, skipResult as u, GetResultIsArrayOptions as v, getDataIsArray as w, getPaginate as x, GetResultIsArrayReturn as y };
520
- //# sourceMappingURL=index-DXXeX7lF.d.mts.map
648
+ export { GetDataIsArrayReturn as A, SkipHookName as B, patchBatch as C, getResultIsArray as D, GetResultIsArrayReturn as E, checkContext as F, IsContextOptions as I, isContext as L, defineHooks as M, contextToJson as N, getPaginate as O, CheckContextOptions as P, addToQuery as R, PatchBatchResultItem as S, GetResultIsArrayOptions as T, addSkip as V, skipResult as _, queryHasProperty as a, replaceData as b, walkQuery as c, WaitForServiceEventOptions as d, WaitForServiceEventResult as f, toPaginated as g, transformParams as h, queryDefaults as i, getDataIsArray as j, getExposedMethods as k, ServiceEventName as l, sortQueryProperties as m, ZipDataResultOptions as n, WalkQueryCallback as o, waitForServiceEvent as p, zipDataResult as r, WalkQueryOptions as s, ZipDataResultItem as t, WaitForServiceEventDefaults as u, ReplaceResultOptions as v, iterateFind as w, PatchBatchOptions as x, replaceResult as y, chunkFind as z };
649
+ //# sourceMappingURL=index-CKAzIogj.d.mts.map
package/dist/index.d.mts CHANGED
@@ -3,8 +3,8 @@ import { C as InferResultFromPath, D as InferUpdateResultFromPath, E as InferUpd
3
3
  import { a as IffHook, i as skippable, n as ThrowIfOptions, o as iff, r as throwIf, s as iffElse, t as unless } from "./unless.hook-CVw1wX_x.mjs";
4
4
  import { i as transformData, n as transformResult, r as transformQuery, t as TransformResultOptions } from "./transform-result.hook-C9wMLWs2.mjs";
5
5
  import { i as resolveData, n as resolveQuery, r as resolveResult, t as resolve } from "./resolve-Cmxskunj.mjs";
6
- import { CacheEvent, CacheOptions, CheckMultiOptions, CreateRelatedOptions, HookSetDataOptions, OnDeleteAction, OnDeleteOptions, ParamsForServerOptions, PreventChangesOptions, RateLimitOptions, SetFieldOptions, SetResultOptions, SoftDeleteOptionFunction, SoftDeleteOptions, StashableOptions, ThrowIfIsIsProviderOptions, ThrowIfIsMultiOptions, TraverseOptions, cache, checkMulti, checkRequired, combine, createRelated, debug, disablePagination, disallow, onDelete, paramsForServer, paramsFromClient, paramsFromClientOptions, preventChanges, rateLimit, setData, setField, setResult, setSlug, softDelete, stashable, throwIfIsMulti, throwIfIsProvider, traverse } from "./hooks.mjs";
7
- import { C as GetDataIsArrayReturn, D as CheckContextOptions, E as contextToJson, M as chunkFind, N as SkipHookName, O as checkContext, P as addSkip, S as getExposedMethods, T as defineHooks, _ as iterateFind, a as WalkQueryOptions, b as getResultIsArray, c as transformParams, d as ReplaceResultOptions, f as replaceResult, g as patchBatch, h as PatchBatchResultItem, i as WalkQueryCallback, j as addToQuery, l as toPaginated, m as PatchBatchOptions, n as ZipDataResultOptions, o as walkQuery, p as replaceData, r as zipDataResult, s as sortQueryProperties, t as ZipDataResultItem, u as skipResult, v as GetResultIsArrayOptions, w as getDataIsArray, x as getPaginate, y as GetResultIsArrayReturn } from "./index-DXXeX7lF.mjs";
6
+ import { CacheEvent, CacheOptions, CheckMultiOptions, CreateRelatedOptions, FindOrCreateOptions, HookSetDataOptions, MuteEventOptions, OnDeleteAction, OnDeleteOptions, ParamsForServerOptions, PreventChangesOptions, RateLimitOptions, SetFieldOptions, SetResultOptions, SoftDeleteOptionFunction, SoftDeleteOptions, StashableOptions, ThrowIfIsIsProviderOptions, ThrowIfIsMultiOptions, TraverseOptions, cache, checkMulti, checkRequired, combine, createRelated, debug, disablePagination, disallow, findOrCreate, muteEvent, onDelete, paramsForServer, paramsFromClient, paramsFromClientOptions, preventChanges, rateLimit, setData, setField, setQueryDefaults, setResult, setSlug, softDelete, stashable, throwIfIsMulti, throwIfIsProvider, traverse } from "./hooks.mjs";
7
+ 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";
8
8
  import { n as mutateResult, r as mutateData, t as MutateResultOptions } from "./mutate-result.util-DR9JMsvl.mjs";
9
9
  import { Paginated, PaginationOptions } from "@feathersjs/feathers";
10
10
 
@@ -15,5 +15,5 @@ type PaginatedOrArray<R, P = undefined> = P extends {
15
15
  paginate: PaginationOptions;
16
16
  } ? Paginated<R> : Paginated<R> | R[];
17
17
  //#endregion
18
- export { CacheEvent, CacheOptions, CheckContextOptions, CheckMultiOptions, ContextFunction, ContextFunctionAsync, ContextFunctionSync, CreateRelatedOptions, DataSingleHookContext, DefaultsInput, DispatchOption, FieldKey, GetDataIsArrayReturn, GetResultIsArrayOptions, GetResultIsArrayReturn, GetService, HookFunction, HookSetDataOptions, HookType, IffHook, InferCreateData, InferCreateDataFromPath, InferCreateDataSingle, InferCreateDataSingleFromPath, InferCreateResult, InferCreateResultFromPath, InferCreateResultSingle, InferCreateResultSingleFromPath, InferDataFromPath, InferFindParams, InferFindResult, InferFindResultFromPath, InferFindResultSingle, InferGetResult, InferGetResultFromPath, InferPatchData, InferPatchDataFromPath, InferPatchResult, InferPatchResultFromPath, InferRemoveResult, InferRemoveResultFromPath, InferResultFromPath, InferUpdateData, InferUpdateDataFromPath, InferUpdateResult, InferUpdateResultFromPath, MethodName, MutateResultOptions, OnDeleteAction, OnDeleteOptions, PaginatedOrArray, ParamsForServerOptions, PatchBatchOptions, PatchBatchResultItem, PredicateContextAsync, PredicateContextSync, PredicateFn, PredicateItemWithContext, PreventChangesOptions, RateLimitOptions, ReplaceResultOptions, ResultSingleHookContext, SetFieldOptions, SetResultOptions, SkipHookName, SoftDeleteOptionFunction, SoftDeleteOptions, StashableOptions, StringFieldKey, ThrowIfIsIsProviderOptions, ThrowIfIsMultiOptions, ThrowIfOptions, TransformParamsFn, TransformResultOptions, TransformerFn, TransformerInputFn, TransportName, TraverseOptions, UnwrapPaginated, UnwrapPaginatedOrArray, WalkQueryCallback, WalkQueryOptions, ZipDataResultItem, ZipDataResultOptions, addSkip, addToQuery, cache, checkContext, checkMulti, checkRequired, chunkFind, combine, contextToJson, createRelated, debug, defineHooks, disablePagination, disallow, getDataIsArray, getExposedMethods, getPaginate, getResultIsArray, hookTypes, iff, iff as when, iffElse, iterateFind, methodNames, mutateData, mutateResult, onDelete, paramsForServer, paramsFromClient, paramsFromClientOptions, patchBatch, preventChanges, rateLimit, replaceData, replaceResult, resolve, resolveData, resolveQuery, resolveResult, setData, setField, setResult, setSlug, skipResult, skippable, softDelete, sortQueryProperties, stashable, throwIf, throwIfIsMulti, throwIfIsProvider, toPaginated, transformData, transformParams, transformQuery, transformResult, traverse, unless, walkQuery, zipDataResult };
18
+ export { CacheEvent, CacheOptions, CheckContextOptions, CheckMultiOptions, ContextFunction, ContextFunctionAsync, ContextFunctionSync, CreateRelatedOptions, DataSingleHookContext, DefaultsInput, DispatchOption, FieldKey, FindOrCreateOptions, GetDataIsArrayReturn, GetResultIsArrayOptions, GetResultIsArrayReturn, GetService, HookFunction, HookSetDataOptions, HookType, IffHook, InferCreateData, InferCreateDataFromPath, InferCreateDataSingle, InferCreateDataSingleFromPath, InferCreateResult, InferCreateResultFromPath, InferCreateResultSingle, InferCreateResultSingleFromPath, InferDataFromPath, InferFindParams, InferFindResult, InferFindResultFromPath, InferFindResultSingle, InferGetResult, InferGetResultFromPath, InferPatchData, InferPatchDataFromPath, InferPatchResult, InferPatchResultFromPath, InferRemoveResult, InferRemoveResultFromPath, InferResultFromPath, InferUpdateData, InferUpdateDataFromPath, InferUpdateResult, InferUpdateResultFromPath, MethodName, MutateResultOptions, MuteEventOptions, OnDeleteAction, OnDeleteOptions, PaginatedOrArray, ParamsForServerOptions, PatchBatchOptions, PatchBatchResultItem, PredicateContextAsync, PredicateContextSync, PredicateFn, PredicateItemWithContext, PreventChangesOptions, RateLimitOptions, ReplaceResultOptions, ResultSingleHookContext, ServiceEventName, SetFieldOptions, SetResultOptions, SkipHookName, SoftDeleteOptionFunction, SoftDeleteOptions, StashableOptions, StringFieldKey, ThrowIfIsIsProviderOptions, ThrowIfIsMultiOptions, ThrowIfOptions, TransformParamsFn, TransformResultOptions, TransformerFn, TransformerInputFn, TransportName, TraverseOptions, UnwrapPaginated, UnwrapPaginatedOrArray, WaitForServiceEventDefaults, WaitForServiceEventOptions, WaitForServiceEventResult, WalkQueryCallback, WalkQueryOptions, ZipDataResultItem, ZipDataResultOptions, addSkip, addToQuery, cache, checkContext, checkMulti, checkRequired, chunkFind, combine, contextToJson, createRelated, debug, defineHooks, disablePagination, disallow, findOrCreate, getDataIsArray, getExposedMethods, getPaginate, getResultIsArray, hookTypes, iff, iff as when, iffElse, iterateFind, methodNames, mutateData, mutateResult, muteEvent, onDelete, paramsForServer, paramsFromClient, paramsFromClientOptions, patchBatch, preventChanges, queryDefaults, queryHasProperty, rateLimit, replaceData, replaceResult, resolve, resolveData, resolveQuery, resolveResult, setData, setField, setQueryDefaults, setResult, setSlug, skipResult, skippable, softDelete, sortQueryProperties, stashable, throwIf, throwIfIsMulti, throwIfIsProvider, toPaginated, transformData, transformParams, transformQuery, transformResult, traverse, unless, waitForServiceEvent, walkQuery, zipDataResult };
19
19
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { _ as combine, a as iffElse, f as getPaginate, i as iff, n as skippable, r as unless, t as throwIf } from "./predicates-puYa4nkf.mjs";
2
- import { a as skipResult, c as getExposedMethods, d as checkContext, f as addToQuery, h as sortQueryProperties, i as toPaginated, l as defineHooks, m as addSkip, n as walkQuery, o as patchBatch, p as chunkFind, r as transformParams, s as iterateFind, t as zipDataResult, u as contextToJson } from "./utils-C2GLf_mV.mjs";
3
- import { _ as debug, a as softDelete, b as checkMulti, c as setField, d as preventChanges, f as paramsFromClient, g as disablePagination, h as disallow, i as stashable, l as setData, m as onDelete, n as throwIfIsProvider, o as setSlug, p as paramsForServer, r as throwIfIsMulti, s as setResult, t as traverse, u as rateLimit, v as createRelated, x as cache, y as checkRequired } from "./hooks-DzXD-3FR.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
+ import { C as checkMulti, S as checkRequired, _ as disallow, a as softDelete, b as debug, c as setQueryDefaults, d as rateLimit, f as preventChanges, g as muteEvent, h as onDelete, i as stashable, l as setField, m as paramsForServer, n as throwIfIsProvider, o as setSlug, p as paramsFromClient, r as throwIfIsMulti, s as setResult, t as traverse, u as setData, v as disablePagination, w as cache, x as createRelated, y as findOrCreate } from "./hooks-tfw03iVM.mjs";
4
4
  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";
5
5
  import { n as transformQuery, r as transformData, t as transformResult } from "./transform-result.hook-B65pTRJO.mjs";
6
6
  import { i as resolveData, n as resolveQuery, r as resolveResult, t as resolve } from "./resolve-B9hRleHY.mjs";
@@ -20,6 +20,6 @@ const methodNames = [
20
20
  "remove"
21
21
  ];
22
22
  //#endregion
23
- export { addSkip, addToQuery, cache, checkContext, checkMulti, checkRequired, chunkFind, combine, contextToJson, createRelated, debug, defineHooks, disablePagination, disallow, getDataIsArray, getExposedMethods, getPaginate, getResultIsArray, hookTypes, iff, iff as when, iffElse, iterateFind, methodNames, mutateData, mutateResult, onDelete, paramsForServer, paramsFromClient, patchBatch, preventChanges, rateLimit, replaceData, replaceResult, resolve, resolveData, resolveQuery, resolveResult, setData, setField, setResult, setSlug, skipResult, skippable, softDelete, sortQueryProperties, stashable, throwIf, throwIfIsMulti, throwIfIsProvider, toPaginated, transformData, transformParams, transformQuery, transformResult, traverse, unless, walkQuery, zipDataResult };
23
+ export { addSkip, addToQuery, cache, checkContext, checkMulti, checkRequired, chunkFind, combine, contextToJson, createRelated, debug, defineHooks, disablePagination, disallow, findOrCreate, getDataIsArray, getExposedMethods, getPaginate, getResultIsArray, hookTypes, iff, iff as when, iffElse, iterateFind, methodNames, mutateData, mutateResult, muteEvent, onDelete, paramsForServer, paramsFromClient, patchBatch, preventChanges, queryDefaults, queryHasProperty, rateLimit, replaceData, replaceResult, resolve, resolveData, resolveQuery, resolveResult, setData, setField, setQueryDefaults, setResult, setSlug, skipResult, skippable, softDelete, sortQueryProperties, stashable, throwIf, throwIfIsMulti, throwIfIsProvider, toPaginated, transformData, transformParams, transformQuery, transformResult, traverse, unless, waitForServiceEvent, walkQuery, zipDataResult };
24
24
 
25
25
  //# sourceMappingURL=index.mjs.map
@@ -1,6 +1,6 @@
1
1
  import { S as TransportName, g as PredicateFn } from "./hook-context-BsxU1vfN.mjs";
2
2
  import { a as IffHook, i as skippable, n as ThrowIfOptions, o as iff, r as throwIf, s as iffElse, t as unless } from "./unless.hook-CVw1wX_x.mjs";
3
- import { A as isContext, N as SkipHookName, k as IsContextOptions } from "./index-DXXeX7lF.mjs";
3
+ import { B as SkipHookName, I as IsContextOptions, L as isContext } from "./index-CKAzIogj.mjs";
4
4
  import { HookContext } from "@feathersjs/feathers";
5
5
 
6
6
  //#region src/predicates/and/and.predicate.d.ts
@@ -3,8 +3,8 @@ import { r as mutateData, t as mutateResult } from "./mutate-result.util-Dqzepn1
3
3
  import { n as transformQuery, r as transformData, t as transformResult } from "./transform-result.hook-B65pTRJO.mjs";
4
4
  import { BadRequest } from "@feathersjs/errors";
5
5
  import _get from "lodash/get.js";
6
- import _omit from "lodash/omit.js";
7
6
  import _set from "lodash/set.js";
7
+ import _omit from "lodash/omit.js";
8
8
  import _pick from "lodash/pick.js";
9
9
  //#region src/transformers/defaults/defaults.transformer.ts
10
10
  /**
@@ -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";
@@ -452,8 +453,92 @@ const transformParams = (params, fn) => {
452
453
  return fn({ ...params }) ?? params;
453
454
  };
454
455
  //#endregion
456
+ //#region src/utils/wait-for-service-event/wait-for-service-event.util.ts
457
+ /**
458
+ * Wait for a service event to fire and resolve with the emitted record. Useful
459
+ * in tests to await the result of an asynchronous service event, a bit like
460
+ * `promisify` for Feathers events.
461
+ *
462
+ * Curried: bind the `app` (and optional defaults) once, then call the returned
463
+ * function per service/event. Resolves with a `[data, { event, context }]`
464
+ * tuple: `data` is typed as the service's record type, and `event` is the union
465
+ * of the requested events.
466
+ *
467
+ * Feathers emits events as `emit(event, record, context)` and fires one event
468
+ * per record, so each resolution carries a single record and its `HookContext`.
469
+ *
470
+ * @example
471
+ * ```ts
472
+ * import { waitForServiceEvent } from 'feathers-utils/utils'
473
+ *
474
+ * const app = feathers()
475
+ * const waitForEvent = waitForServiceEvent(app)
476
+ *
477
+ * // Wait for the next `users` record to be created.
478
+ * const [user] = await waitForEvent('users', 'created')
479
+ *
480
+ * // Wait for a specific record, with a custom timeout and filter.
481
+ * const [data, { event }] = await waitForEvent(
482
+ * 'users',
483
+ * ['created', 'patched'],
484
+ * { filter: (user) => user.email === 'jane@example.com', timeout: 1000 },
485
+ * )
486
+ * ```
487
+ *
488
+ * @see https://utils.feathersjs.com/utils/wait-for-service-event.html
489
+ */
490
+ function waitForServiceEvent(app, defaultOptions) {
491
+ return function waitForEvent(servicePath, eventOrEvents, options) {
492
+ const events = Array.isArray(eventOrEvents) ? eventOrEvents : [eventOrEvents];
493
+ const timeout = options?.timeout ?? defaultOptions?.timeout ?? 5e3;
494
+ const filter = options?.filter;
495
+ const signal = options?.signal ?? defaultOptions?.signal;
496
+ const service = app.service(servicePath);
497
+ return new Promise((resolve, reject) => {
498
+ let timer;
499
+ const listeners = events.map((event) => {
500
+ const listener = (data, context) => {
501
+ if (filter && !filter(data, context)) return;
502
+ cleanup();
503
+ resolve([data, {
504
+ event,
505
+ context
506
+ }]);
507
+ };
508
+ return [event, listener];
509
+ });
510
+ const abortError = () => signal?.reason ?? /* @__PURE__ */ new Error(`Aborted waiting for event "${events.join(", ")}" on service "${String(servicePath)}"`);
511
+ const onAbort = () => {
512
+ cleanup();
513
+ reject(abortError());
514
+ };
515
+ function cleanup() {
516
+ if (timer) {
517
+ clearTimeout(timer);
518
+ timer = void 0;
519
+ }
520
+ for (const [event, listener] of listeners) service.off(event, listener);
521
+ signal?.removeEventListener("abort", onAbort);
522
+ }
523
+ if (signal?.aborted) {
524
+ reject(abortError());
525
+ return;
526
+ }
527
+ if (timeout !== false) timer = setTimeout(() => {
528
+ cleanup();
529
+ reject(/* @__PURE__ */ new Error(`Timeout waiting for event "${events.join(", ")}" on service "${String(servicePath)}"`));
530
+ }, timeout);
531
+ signal?.addEventListener("abort", onAbort, { once: true });
532
+ for (const [event, listener] of listeners) service.on(event, listener);
533
+ });
534
+ };
535
+ }
536
+ //#endregion
455
537
  //#region src/utils/walk-query/walk-query.util.ts
456
- const _walkQueryUtil = (query, walker, options) => {
538
+ const _walkQueryUtil = (query, walker, state, options) => {
539
+ const stop = () => {
540
+ state.stopped = true;
541
+ };
457
542
  let cloned = false;
458
543
  const clonedSecond = {};
459
544
  function set(key, value, secondKey) {
@@ -471,64 +556,75 @@ const _walkQueryUtil = (query, walker, options) => {
471
556
  }
472
557
  query[key] = value;
473
558
  }
474
- for (const key in query) if ((key === "$or" || key === "$and" || key === "$nor") && Array.isArray(query[key])) {
475
- let array = query[key];
476
- let copiedArray = false;
477
- for (let i = 0, n = array.length; i < n; i++) {
478
- const nestedQuery = array[i];
479
- const transformed = _walkQueryUtil(nestedQuery, walker, {
480
- ...options,
481
- path: [
482
- ...options?.path || [],
483
- key,
484
- i
485
- ]
486
- });
487
- if (transformed !== nestedQuery) {
488
- if (!copiedArray) {
489
- array = [...array];
490
- 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;
491
581
  }
492
- array[i] = transformed;
493
582
  }
494
- }
495
- if (copiedArray) set(key, array);
496
- } else if (typeof query[key] === "object" && query[key] !== null && !Array.isArray(query[key])) {
497
- let hasOperator = false;
498
- for (const operator in query[key]) if (operator.startsWith("$")) {
499
- hasOperator = true;
500
- const value = walker({
501
- operator,
502
- path: [...options?.path ?? [], key],
503
- property: key,
504
- value: query[key][operator]
505
- });
506
- if (value !== void 0 && value !== query[key][operator]) set(key, value, operator);
507
- }
508
- 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 {
509
611
  const value = walker({
510
612
  operator: void 0,
511
613
  path: [...options?.path ?? [], key],
512
614
  property: key,
513
- value: query[key]
615
+ value: query[key],
616
+ stop
514
617
  });
515
618
  if (value !== void 0 && value !== query[key]) set(key, value);
516
619
  }
517
- } else {
518
- const value = walker({
519
- operator: void 0,
520
- path: [...options?.path ?? [], key],
521
- property: key,
522
- value: query[key]
523
- });
524
- if (value !== void 0 && value !== query[key]) set(key, value);
525
620
  }
526
621
  return query;
527
622
  };
528
623
  /**
529
624
  * Walks every property of a Feathers query (including nested `$and`/`$or`/`$nor` arrays)
530
625
  * and calls the `walker` function for each one. The walker receives the property name, operator,
531
- * 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.
532
628
  *
533
629
  * @example
534
630
  * ```ts
@@ -540,10 +636,79 @@ const _walkQueryUtil = (query, walker, options) => {
540
636
  * // => { age: { $gt: 18 } }
541
637
  * ```
542
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
+ *
543
651
  * @see https://utils.feathersjs.com/utils/walk-query.html
544
652
  */
545
653
  const walkQuery = (query, walker) => {
546
- 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);
547
712
  };
548
713
  //#endregion
549
714
  //#region src/utils/zip-data-result/zip-data-result.util.ts
@@ -584,6 +749,6 @@ function zipDataResult(context, options) {
584
749
  return result;
585
750
  }
586
751
  //#endregion
587
- export { skipResult as a, getExposedMethods as c, checkContext as d, addToQuery as f, sortQueryProperties as h, toPaginated as i, defineHooks as l, addSkip as m, walkQuery as n, patchBatch as o, chunkFind as p, transformParams as r, iterateFind as s, zipDataResult as t, contextToJson 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 };
588
753
 
589
- //# sourceMappingURL=utils-C2GLf_mV.mjs.map
754
+ //# sourceMappingURL=utils-BAIcSl7u.mjs.map