Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion docs/.vitepress/plugins/utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,15 @@ ${utility.examples.join('\n\n')}

if (utility.dts) {
code.push(`## Type declaration
<details>
<summary class="opacity-50 italic cursor-pointer select-none">Show Type Declarations</summary>
\`\`\`ts
${utility.dts}
\`\`\` `)
\`\`\`
</details>
`)
}

if (utility.args?.length) {
Expand Down
10 changes: 10 additions & 0 deletions src/common/clone.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
/**
* Deep-clones an object using `JSON.parse(JSON.stringify(...))`.
* Simple and fast for JSON-serializable data, but does not handle
* `Date` objects, `undefined` values, functions, or circular references.
*
* @example
* ```ts
* const copy = clone({ name: 'Alice', nested: { value: 1 } })
* ```
*/
export function clone(obj: any) {
return JSON.parse(JSON.stringify(obj))
}
11 changes: 11 additions & 0 deletions src/common/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
/**
* Type guard that checks if a value is a `Promise` instance.
*
* @example
* ```ts
* const result = maybeSyncFn()
* if (isPromise(result)) {
* await result
* }
* ```
*/
export function isPromise(p: any): p is Promise<any> {
return p instanceof Promise
}
Expand Down
9 changes: 9 additions & 0 deletions src/common/traverse.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import _traverse from 'neotraverse'

/**
* Recursively traverses items (or an array of items) and applies a converter function
* to every node using `neotraverse`. Modifications happen in place.
*
* @example
* ```ts
* traverse(items, function () { if (this.key === 'secret') this.remove() })
* ```
*/
export function traverse<T extends Record<string, any>>(
items: T | T[],
converter: (item: T) => void,
Expand Down
17 changes: 16 additions & 1 deletion src/hooks/cache/cache.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,22 @@ export type CacheOptions = {
}

/**
* A hook to cache `get` and `find` method results based on `params`.
* Caches `get` and `find` results based on `params`. On mutating methods (`create`, `update`,
* `patch`, `remove`), affected cache entries are automatically invalidated.
* Works as a `before`, `after`, or `around` hook.
*
* @example
* ```ts
* import { cache } from 'feathers-utils/hooks'
*
* const myCache = new Map()
*
* app.service('users').hooks({
* around: {
* all: [cache({ map: myCache, transformParams: (params) => ({ query: params.query }) })]
* }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/cache.html
*/
Expand Down
13 changes: 12 additions & 1 deletion src/hooks/check-multi/check-multi.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,18 @@ export type CheckMultiOptions = {
}

/**
* Check if the 'multi' option is set for a method. You can use this to early throw an error if 'multi' is not set.
* Checks if the `multi` option is enabled for the current method and throws a
* `MethodNotAllowed` error if multi operations are not permitted.
* Useful to guard against accidental bulk `create`, `patch`, or `remove` calls.
*
* @example
* ```ts
* import { checkMulti } from 'feathers-utils/hooks'
*
* app.service('users').hooks({
* before: { create: [checkMulti()] }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/check-multi.html
*/
Expand Down
13 changes: 12 additions & 1 deletion src/hooks/check-required/check-required.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,18 @@ import type { MaybeArray } from '../../internal.utils.js'
import { toArray } from '../../internal.utils.js'

/**
* Check selected fields exist and are not falsey. Numeric 0 is acceptable.
* Validates that the specified fields exist on `context.data` and are not falsy.
* Numeric `0` and boolean `false` are treated as valid values.
* Throws a `BadRequest` error if any required field is missing or null.
*
* @example
* ```ts
* import { checkRequired } from 'feathers-utils/hooks'
*
* app.service('users').hooks({
* before: { create: [checkRequired(['email', 'password'])] }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/check-required.html
*/
Expand Down
15 changes: 14 additions & 1 deletion src/hooks/create-related/create-related.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,20 @@ export interface CreateRelatedOptions<S = Record<string, any>> {
}

/**
* Create related records in other services.
* Creates related records in other services after a successful `create` call.
* For each result item, a `data` function produces the record to create in the target service.
* Supports creating records one-by-one or in a single multi-create when `multi: true`.
*
* @example
* ```ts
* import { createRelated } from 'feathers-utils/hooks'
*
* app.service('users').hooks({
* after: {
* create: [createRelated({ service: 'profiles', data: (user) => ({ userId: user.id }) })]
* }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/create-related.html
*/
Expand Down
13 changes: 12 additions & 1 deletion src/hooks/debug/debug.hook.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import type { HookContext, NextFunction } from '@feathersjs/feathers'

/**
* Display the current hook context for debugging.
* Logs the current hook context to the console for debugging purposes.
* Displays timestamp, service path, method, type, id, data, query, result, and
* any additional param fields you specify.
*
* @example
* ```ts
* import { debug } from 'feathers-utils/hooks'
*
* app.service('users').hooks({
* before: { find: [debug('before find', 'user')] }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/debug.html
*/
Expand Down
12 changes: 12 additions & 0 deletions src/hooks/disable-pagination/disable-pagination.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ import { checkContext } from '../../utils/index.js'

/**
* Disables pagination when `query.$limit` is `-1` or `'-1'`.
* Removes the `$limit` from the query and sets `params.paginate = false`.
* Must be used as a `before` or `around` hook on the `find` method.
*
* @example
* ```ts
* import { disablePagination } from 'feathers-utils/hooks'
*
* app.service('users').hooks({
* before: { find: [disablePagination()] }
* })
* // Then call: app.service('users').find({ query: { $limit: -1 } })
* ```
*
* @see https://utils.feathersjs.com/hooks/disable-pagination.html
*/
Expand Down
14 changes: 14 additions & 0 deletions src/hooks/disallow/disallow.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ import { toArray } from '../../internal.utils.js'

/**
* Prevents access to a service method completely or for specific transports.
* When called without arguments, the method is blocked for all callers.
* When called with transport names, only those transports are blocked.
*
* @example
* ```ts
* import { disallow } from 'feathers-utils/hooks'
*
* app.service('users').hooks({
* before: {
* remove: [disallow('external')], // block external access
* update: [disallow()], // block completely
* }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/disallow.html
*/
Expand Down
15 changes: 14 additions & 1 deletion src/hooks/iff-else/iff-else.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,20 @@ import { combine } from '../../utils/combine/combine.util.js'
import type { HookFunction, PredicateFn } from '../../types.js'

/**
* Execute one array of hooks or another based on a sync or async predicate.
* Executes one array of hooks when the predicate is truthy, or another array when it is falsy.
* The predicate can be a boolean or a sync/async function.
* Unlike `iff`, both branches are provided upfront without chaining.
*
* @example
* ```ts
* import { iffElse, isProvider } from 'feathers-utils/predicates'
*
* app.service('users').hooks({
* before: {
* find: [iffElse(isProvider('external'), [hook1()], [hook2()])]
* }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/iff-else.html
*/
Expand Down
17 changes: 13 additions & 4 deletions src/hooks/params-for-server/params-for-server.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,19 @@ export type ParamsForServerOptions = {
}

/**
* a hook to move params to query._$client
* the server only receives 'query' from params. All other params are ignored.
* So, to use `$populateParams` on the server, we need to move the params to query._$client
* the server will move them back to params
* Client-side hook that moves whitelisted `params` properties into `query._$client`
* so they survive the client-to-server transport. The server only receives `query`
* from params — use `paramsFromClient` on the server to restore them.
*
* @example
* ```ts
* import { paramsForServer } from 'feathers-utils/hooks'
*
* // Client-side
* app.service('users').hooks({
* before: { all: [paramsForServer('populateParams')] }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/params-for-server.html
*/
Expand Down
14 changes: 13 additions & 1 deletion src/hooks/params-from-client/params-from-client.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,19 @@ export type paramsFromClientOptions = {
}

/**
* Pass `context.params` from client to server. Server hook.
* Server-side hook that extracts whitelisted properties from `query._$client` back
* into `context.params`. This is the counterpart to `paramsForServer`, which encodes
* params on the client side for transport.
*
* @example
* ```ts
* import { paramsFromClient } from 'feathers-utils/hooks'
*
* // Server-side
* app.service('users').hooks({
* before: { all: [paramsFromClient('populateParams')] }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/params-from-client.html
*/
Expand Down
13 changes: 12 additions & 1 deletion src/hooks/prevent-changes/prevent-changes.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,18 @@ export type PreventChangesOptions = {
}

/**
* Prevent patch service calls from changing certain fields.
* Prevents `patch` calls from modifying certain fields. By default, the protected
* fields are silently removed from `context.data`. When `error` is set, a `BadRequest`
* is thrown if any protected field is present.
*
* @example
* ```ts
* import { preventChanges } from 'feathers-utils/hooks'
*
* app.service('users').hooks({
* before: { patch: [preventChanges(['email', 'role'], { error: true })] }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/prevent-changes.html
*/
Expand Down
13 changes: 12 additions & 1 deletion src/hooks/set-data/set-data.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,18 @@ export interface HookSetDataOptions {
}

/**
* hook to set properties on `context.data`
* Sets a property on each item in `context.data` from another property on the hook context.
* Supports dot-notation paths for both source and target. Throws a `Forbidden` error
* if the source field is missing (unless `allowUndefined` is `true`).
*
* @example
* ```ts
* import { setData } from 'feathers-utils/hooks'
*
* app.service('posts').hooks({
* before: { create: [setData('params.user.id', 'userId')] }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/set-data.html
*/
Expand Down
13 changes: 12 additions & 1 deletion src/hooks/set-field/set-field.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,18 @@ export interface SetFieldOptions {
}

/**
* The `setField` hook allows to set a field on the hook context based on the value of another field on the hook context.
* Sets a field on the hook context (e.g. `params.query`) based on the value of another
* context field (e.g. `params.user.id`). Useful for scoping queries to the authenticated user.
* Throws a `Forbidden` error if the source field is missing (unless `allowUndefined` is `true`).
*
* @example
* ```ts
* import { setField } from 'feathers-utils/hooks'
*
* app.service('posts').hooks({
* before: { all: [setField({ from: 'params.user.id', as: 'params.query.userId' })] }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/set-field.html
*/
Expand Down
13 changes: 12 additions & 1 deletion src/hooks/set-result/set-result.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,18 @@ export interface SetResultOptions {
}

/**
* hook to set properties on `context.result`
* Sets a property on each item in `context.result` from another property on the hook context.
* Supports dot-notation paths for both source and target, and can optionally
* operate on `context.dispatch` as well.
*
* @example
* ```ts
* import { setResult } from 'feathers-utils/hooks'
*
* app.service('posts').hooks({
* after: { all: [setResult('params.user.id', 'currentUserId')] }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/set-result.html
*/
Expand Down
13 changes: 12 additions & 1 deletion src/hooks/set-slug/set-slug.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@ import _set from 'lodash/set.js'
import type { HookContext, NextFunction } from '@feathersjs/feathers'

/**
* Fix slugs in URL, e.g. /stores/:storeId.
* Extracts URL route parameters (slugs) and sets them on `params.query`.
* For example, given a route `/stores/:storeId`, this hook copies the resolved
* `storeId` value from `params.route` into the query. Only applies to the `rest` provider.
*
* @example
* ```ts
* import { setSlug } from 'feathers-utils/hooks'
*
* app.service('stores/:storeId/products').hooks({
* before: { all: [setSlug('storeId')] }
* })
* ```
*
* @see https://utils.feathersjs.com/hooks/set-slug.html
*/
Expand Down
11 changes: 10 additions & 1 deletion src/hooks/skippable/skippable.hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@ import type { HookContext, NextFunction } from '@feathersjs/feathers'
import type { HookFunction, PredicateFn } from '../../types.js'

/**
* Wrap a hook to make it skippable
* Wraps a hook so it can be conditionally skipped based on a predicate.
* When the predicate returns `true`, the wrapped hook is skipped entirely.
* Commonly used with `shouldSkip` and `addSkip` for runtime hook control.
*
* @example
* ```ts
* import { skippable, shouldSkip } from 'feathers-utils/predicates'
*
* const myHook = skippable(someHook(), shouldSkip('someHook'))
* ```
*
* @see https://utils.feathersjs.com/hooks/skippable.html
*/
Expand Down
Loading
Loading