| H | void;
+
+export type TransformParamsFn = (params: P) => P | void;
+
+export type DispatchOption = boolean | 'both';
diff --git a/test/utils/calling-params-1.test.ts b/src/utils/calling-params/calling-params-1.test.ts
similarity index 98%
rename from test/utils/calling-params-1.test.ts
rename to src/utils/calling-params/calling-params-1.test.ts
index cd50d5db..c70879e8 100755
--- a/test/utils/calling-params-1.test.ts
+++ b/src/utils/calling-params/calling-params-1.test.ts
@@ -1,5 +1,5 @@
import { assert } from 'vitest';
-import { callingParamsDefaults, callingParams } from '../../src';
+import { callingParamsDefaults, callingParams } from './calling-params';
let context1: any;
let context2: any;
diff --git a/test/utils/calling-params-2.test.ts b/src/utils/calling-params/calling-params-2.test.ts
similarity index 97%
rename from test/utils/calling-params-2.test.ts
rename to src/utils/calling-params/calling-params-2.test.ts
index 9303e094..1b0b2e8a 100755
--- a/test/utils/calling-params-2.test.ts
+++ b/src/utils/calling-params/calling-params-2.test.ts
@@ -1,5 +1,5 @@
import { assert } from 'vitest';
-import { makeCallingParams } from '../../src';
+import { makeCallingParams } from './calling-params';
let context: any;
diff --git a/src/utils/calling-params.ts b/src/utils/calling-params/calling-params.ts
similarity index 90%
rename from src/utils/calling-params.ts
rename to src/utils/calling-params/calling-params.ts
index fdd58d31..8a1e08b8 100755
--- a/src/utils/calling-params.ts
+++ b/src/utils/calling-params/calling-params.ts
@@ -58,14 +58,15 @@ export function callingParamsDefaults(propNames: string[], newProps?: any): void
* Build params for a service call. (Utility function.)
* @see https://hooks-common.feathersjs.com/utilities.html#callingparams
*/
-export function callingParams({
- query,
- propNames = [],
- newProps = {},
- hooksToDisable = [],
- ignoreDefaults,
-}: CallingParamsOptions = {}) {
- return (context: H) => {
+export const callingParams =
+ ({
+ query,
+ propNames = [],
+ newProps = {},
+ hooksToDisable = [],
+ ignoreDefaults,
+ }: CallingParamsOptions = {}) =>
+ (context: H) => {
propNames = Array.isArray(propNames) ? propNames : [propNames];
hooksToDisable = Array.isArray(hooksToDisable) ? hooksToDisable : [hooksToDisable];
@@ -89,7 +90,7 @@ export function callingParams({
switch (name) {
case 'populate': // fall through
case 'fastJoin':
- // @ts-ignore
+ // @ts-expect-error TODO
newParams._populate = 'skip';
break;
case 'softDelete':
@@ -97,15 +98,15 @@ export function callingParams({
newParams.query.$disableSoftDelete = true;
break;
case 'softDelete2':
- // @ts-ignore
+ // @ts-expect-error TODO
newParams.$disableSoftDelete2 = true;
break;
case 'ignoreDeletedAt':
- // @ts-ignore
+ // @ts-expect-error TODO
newParams.$ignoreDeletedAt = true;
break;
case 'stashBefore':
- // @ts-ignore
+ // @ts-expect-error TODO
newParams.disableStashBefore = true;
break;
}
@@ -113,7 +114,6 @@ export function callingParams({
return newParams;
};
-}
/**
* You should prefer using the callingParams utility to makeCallingParams.
diff --git a/src/utils/check-context-if.ts b/src/utils/check-context-if.ts
index 23b3f2ef..0a0e4672 100755
--- a/src/utils/check-context-if.ts
+++ b/src/utils/check-context-if.ts
@@ -1,6 +1,6 @@
import type { HookContext } from '@feathersjs/feathers';
import type { MethodName, HookType } from '../types';
-import { checkContext } from './check-context';
+import { checkContext } from './check-context/check-context';
// TODO: Add checkContextIf to docs
/**
diff --git a/src/utils/check-context.ts b/src/utils/check-context.ts
deleted file mode 100755
index 54fc9e45..00000000
--- a/src/utils/check-context.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import type { HookContext } from '@feathersjs/feathers';
-import { methodNames } from '../types';
-import type { HookType, MethodName } from '../types';
-
-/**
- * Restrict a hook to run for certain methods and method types. (Utility function.)
- * @see https://hooks-common.feathersjs.com/utilities.html#checkcontext
- */
-export function checkContext(
- context: H,
- type?: HookType | HookType[] | null,
- methods?: MethodName | MethodName[] | null,
- label = 'anonymous',
-): void {
- if (type) {
- const types = Array.isArray(type) ? type : [type]; // safe enough for allowed values
- if (!types.includes(context.type)) {
- throw new Error(`The '${label}' hook can only be used as a '${type}' hook.`);
- }
- }
-
- if (!methods) {
- return;
- }
- if (!methodNames.includes(context.method as any)) {
- return;
- } // allow custom methods
-
- const methodsArr = Array.isArray(methods) ? methods : [methods]; // safe enough for allowed values
-
- if (methodsArr.length > 0 && !methodsArr.includes(context.method as any)) {
- const msg = JSON.stringify(methodsArr);
- throw new Error(`The '${label}' hook can only be used on the '${msg}' service method(s).`);
- }
-}
diff --git a/src/utils/check-context/check-context.test.ts b/src/utils/check-context/check-context.test.ts
new file mode 100755
index 00000000..52388ba3
--- /dev/null
+++ b/src/utils/check-context/check-context.test.ts
@@ -0,0 +1,77 @@
+import { assert, expect } from 'vitest';
+
+import { checkContext } from './check-context';
+import { HookContext } from '@feathersjs/feathers';
+
+const make = (type: any, method: any) => ({ type, method }) as HookContext;
+
+describe('util checkContext', () => {
+ it('handles "any" type and method', () => {
+ expect(() => checkContext(make('before', 'create'))).not.toThrow();
+ });
+
+ it('handles expected type', () => {
+ expect(() => checkContext(make('before', 'create'), 'before')).not.toThrow();
+ });
+
+ it('handles unexpected type', () => {
+ expect(() => checkContext(make('after', 'create'), 'before')).toThrow();
+ });
+
+ it('handles undefined type', () => {
+ expect(() => checkContext(make('after', 'create'), undefined, 'create')).not.toThrow();
+ });
+
+ it('handles null type', () => {
+ expect(() => checkContext(make('after', 'create'), null, 'create')).not.toThrow();
+ });
+
+ it('handles expected type as array', () => {
+ expect(() => checkContext(make('before', 'create'), ['before', 'after'])).not.toThrow();
+ });
+
+ it('handles unexpected type as array', () => {
+ expect(() => checkContext(make('error', 'create'), ['before', 'after'])).toThrow();
+ });
+
+ it('handles expected method as string', () => {
+ expect(() => checkContext(make('before', 'create'), null, 'create')).not.toThrow();
+ });
+
+ it('handles unexpected method as string', () => {
+ expect(() => checkContext(make('before', 'patch'), null, 'create')).toThrow();
+ });
+
+ it('handles expected method as array', () => {
+ expect(() =>
+ checkContext(make('before', 'create'), null, ['create', 'update', 'remove']),
+ ).not.toThrow();
+ });
+
+ it('handles unexpected method as array', () => {
+ expect(() =>
+ checkContext(make('before', 'patch'), null, ['create', 'update', 'remove']),
+ ).toThrow();
+ });
+
+ it('handles undefined method', () => {
+ expect(() => checkContext(make('before', 'patch'), null, undefined)).not.toThrow();
+ });
+
+ it('handles null method', () => {
+ expect(() => checkContext(make('before', 'patch'), null, null)).not.toThrow();
+ });
+
+ it('handles expected type and method as array', () => {
+ expect(() =>
+ checkContext(make('before', 'create'), ['before', 'after'], ['create', 'update', 'remove']),
+ ).not.toThrow();
+ });
+
+ it('allows custom methods', () => {
+ expect(() => checkContext(make('before', 'custom'), 'before', 'create')).toThrow();
+ expect(() =>
+ checkContext(make('before', 'custom'), 'before', ['create', 'custom']),
+ ).not.toThrow();
+ });
+});
diff --git a/src/utils/check-context/check-context.ts b/src/utils/check-context/check-context.ts
new file mode 100755
index 00000000..4dbe6b14
--- /dev/null
+++ b/src/utils/check-context/check-context.ts
@@ -0,0 +1,23 @@
+import type { HookContext } from '@feathersjs/feathers';
+import type { HookType, MethodName } from '../../types';
+import { isContext } from '../../predicates/is-context/is-context';
+
+/**
+ * Restrict a hook to run for certain methods and method types. (Utility function.)
+ * @see https://hooks-common.feathersjs.com/utilities.html#checkcontext
+ */
+export function checkContext(
+ context: H,
+ type?: HookType | HookType[] | null,
+ methods?: MethodName | MethodName[] | null,
+ label = 'anonymous',
+): void {
+ if (
+ !isContext({
+ method: methods ?? undefined,
+ type: type ?? undefined,
+ })(context)
+ ) {
+ throw new Error(`The '${label}' hook has invalid context.`);
+ }
+}
diff --git a/test/utils/combine.test.ts b/src/utils/combine/combine.test.ts
similarity index 94%
rename from test/utils/combine.test.ts
rename to src/utils/combine/combine.test.ts
index 32000b5d..f2c347a0 100755
--- a/test/utils/combine.test.ts
+++ b/src/utils/combine/combine.test.ts
@@ -1,7 +1,9 @@
+/* eslint-disable @typescript-eslint/no-this-alias */
import { assert } from 'vitest';
import { feathers } from '@feathersjs/feathers';
import { MemoryService } from '@feathersjs/memory';
-import { combine } from '../../src';
+import { combine } from './combine';
+import { clone } from '../../common';
const startId = 6;
const storeInit = {
@@ -14,13 +16,11 @@ const storeInit = {
};
let store;
-function services(this: any) {
- const app = this;
+function services(app: any) {
app.configure(user);
}
-function user(this: any) {
- const app = this;
+function user(app: any) {
let service: any;
let hookId: any;
let hookData: any;
@@ -150,7 +150,3 @@ describe('util combine', () => {
.catch(() => {});
});
});
-
-function clone(obj: any) {
- return JSON.parse(JSON.stringify(obj));
-}
diff --git a/src/utils/combine.ts b/src/utils/combine/combine.ts
similarity index 83%
rename from src/utils/combine.ts
rename to src/utils/combine/combine.ts
index e1e44eca..8a75aca4 100755
--- a/src/utils/combine.ts
+++ b/src/utils/combine/combine.ts
@@ -1,8 +1,8 @@
import type { HookContext } from '@feathersjs/feathers';
-import type { HookFunction } from '../types';
+import type { HookFunction } from '../../types';
/**
- * Sequentially execute multiple sync or async hooks.
+ * Sequentially execute multiple hooks.
* @see https://hooks-common.feathersjs.com/utilities.html#combine
*/
export function combine(...serviceHooks: HookFunction[]) {
@@ -34,21 +34,20 @@ export function combine(...serviceHooks: Ho
};
// Go through all hooks and chain them into our promise
- // @ts-ignore
+
+ // @ts-expect-error TODO
const promise = serviceHooks.reduce(async (current, fn) => {
- // @ts-ignore
+ // @ts-expect-error TODO
const hook = fn.bind(this);
// Use the returned hook object or the old one
- // eslint-disable-next-line @typescript-eslint/await-thenable
+
const currentHook = await current;
const currentCtx = await hook(currentHook);
- // @ts-ignore
return updateCurrentHook(currentCtx);
}, Promise.resolve(ctx));
try {
- // eslint-disable-next-line @typescript-eslint/await-thenable
await promise;
return ctx;
} catch (error: any) {
diff --git a/src/utils/every.ts b/src/utils/every.ts
deleted file mode 100755
index ad9d6055..00000000
--- a/src/utils/every.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import type { HookContext } from '@feathersjs/feathers';
-import type { AsyncPredicateFn, PredicateFn } from '../types';
-
-/**
- * Return the and of a series of sync or async predicate functions.
- * @see https://hooks-common.feathersjs.com/utilities.html#every
- */
-export function every(
- ...predicates: PredicateFn[]
-): AsyncPredicateFn {
- return async function (this: any, ...fnArgs: any[]) {
- // @ts-ignore
- const promises = predicates.map(fn => fn.apply(this, fnArgs));
-
- const results = await Promise.all(promises);
- return await Promise.resolve(results.every(result => !!result));
- };
-}
diff --git a/src/utils/get-data-is-array/get-data-is-array.test.ts b/src/utils/get-data-is-array/get-data-is-array.test.ts
new file mode 100644
index 00000000..b91a42b6
--- /dev/null
+++ b/src/utils/get-data-is-array/get-data-is-array.test.ts
@@ -0,0 +1,31 @@
+import { getDataIsArray } from './get-data-is-array';
+
+describe('getDataIsArray', () => {
+ it('falsy data', () => {
+ expect(getDataIsArray({ data: null } as any)).toEqual({
+ isArray: false,
+ data: [],
+ });
+
+ expect(getDataIsArray({ data: undefined } as any)).toEqual({
+ isArray: false,
+ data: [],
+ });
+ });
+
+ it('array data', () => {
+ const data = [1, 2, 3];
+ expect(getDataIsArray({ data } as any)).toEqual({
+ isArray: true,
+ data,
+ });
+ });
+
+ it('non-array data', () => {
+ const data = { a: 1, b: 2 };
+ expect(getDataIsArray({ data } as any)).toEqual({
+ isArray: false,
+ data: [data],
+ });
+ });
+});
diff --git a/src/utils/get-data-is-array/get-data-is-array.ts b/src/utils/get-data-is-array/get-data-is-array.ts
new file mode 100644
index 00000000..c7cf9221
--- /dev/null
+++ b/src/utils/get-data-is-array/get-data-is-array.ts
@@ -0,0 +1,19 @@
+import { HookContext } from '@feathersjs/feathers';
+
+export function getDataIsArray(
+ context: H,
+): { data: any[]; isArray: boolean } {
+ if (!context.data) {
+ return {
+ isArray: false,
+ data: [],
+ };
+ }
+
+ const isArray = Array.isArray(context.data);
+
+ return {
+ isArray,
+ data: isArray ? context.data : [context.data],
+ };
+}
diff --git a/src/utils/get-items/get-items.test.ts b/src/utils/get-items/get-items.test.ts
new file mode 100644
index 00000000..f5308746
--- /dev/null
+++ b/src/utils/get-items/get-items.test.ts
@@ -0,0 +1,117 @@
+import { assert } from 'vitest';
+import { getItems } from './get-items';
+
+describe('getItems', () => {
+ it('updates hook before::create item', () => {
+ assert.deepEqual(
+ getItems({
+ type: 'before',
+ method: 'create',
+ params: { provider: 'rest' },
+ data: { first: 'John', last: 'Doe' },
+ } as any),
+ { first: 'John', last: 'Doe' },
+ );
+ });
+
+ it('updates hook before::create items', () => {
+ assert.deepEqual(
+ getItems({
+ type: 'before',
+ method: 'create',
+ params: { provider: 'rest' },
+ data: [
+ { first: 'John', last: 'Doe' },
+ { first: 'Jane', last: 'Doe' },
+ ],
+ } as any),
+ [
+ { first: 'John', last: 'Doe' },
+ { first: 'Jane', last: 'Doe' },
+ ],
+ );
+ });
+
+ it('updates hook after::create item', () => {
+ assert.deepEqual(
+ getItems({
+ type: 'after',
+ method: 'create',
+ params: { provider: 'rest' },
+ result: { first: 'Jane2', last: 'Doe2' },
+ } as any),
+ { first: 'Jane2', last: 'Doe2' },
+ );
+ });
+
+ it('updates hook after::create items', () => {
+ assert.deepEqual(
+ getItems({
+ type: 'after',
+ method: 'create',
+ params: { provider: 'rest' },
+ result: [
+ { first: 'John2', last: 'Doe2' },
+ { first: 'Jane', last: 'Doe' },
+ ],
+ } as any),
+ [
+ { first: 'John2', last: 'Doe2' },
+ { first: 'Jane', last: 'Doe' },
+ ],
+ );
+ });
+
+ it('updates hook after::find item', () => {
+ assert.deepEqual(
+ getItems({
+ type: 'after',
+ method: 'find',
+ params: { provider: 'rest' },
+ result: {
+ total: 2,
+ data: [
+ { first: 'John3', last: 'Doe3' },
+ { first: 'Jane3', last: 'Doe3' },
+ ],
+ },
+ } as any),
+ [
+ { first: 'John3', last: 'Doe3' },
+ { first: 'Jane3', last: 'Doe3' },
+ ],
+ );
+ });
+
+ it('updates hook after::find item paginated', () => {
+ assert.deepEqual(
+ getItems({
+ type: 'after',
+ method: 'find',
+ params: { provider: 'rest' },
+ result: [
+ { first: 'John', last: 'Doe' },
+ { first: 'Jane', last: 'Doe' },
+ ],
+ } as any),
+ [
+ { first: 'John', last: 'Doe' },
+ { first: 'Jane', last: 'Doe' },
+ ],
+ );
+ });
+
+ it('does not throw on before without data', () => {
+ assert.equal(
+ getItems({ type: 'before', method: 'create', params: { provider: 'rest' } } as any),
+ undefined,
+ );
+ });
+
+ it('does not throw on after without data', () => {
+ assert.equal(
+ getItems({ type: 'after', method: 'find', params: { provider: 'rest' } } as any),
+ undefined,
+ );
+ });
+});
diff --git a/src/utils/get-items.ts b/src/utils/get-items/get-items.ts
similarity index 88%
rename from src/utils/get-items.ts
rename to src/utils/get-items/get-items.ts
index de69848b..f3a7591f 100755
--- a/src/utils/get-items.ts
+++ b/src/utils/get-items/get-items.ts
@@ -4,12 +4,12 @@ import type { HookContext } from '@feathersjs/feathers';
* Get the records in context.data or context.result[.data]. (Utility function.)
*
* @see https://hooks-common.feathersjs.com/utilities.html#getitems
+ *
+ * @deprecated Use `getDataIsArray` or `getResultIsArray` instead.
*/
export function getItems(context: H): any {
- // @ts-ignore
if (context.params && context.params._actOn === 'dispatch') return context.dispatch;
const items = context.type === 'before' ? context.data : context.result;
- // @ts-ignore
return items && context.method === 'find' ? items.data || items : items;
}
diff --git a/src/utils/get-paginate/get-paginate.test.ts b/src/utils/get-paginate/get-paginate.test.ts
new file mode 100644
index 00000000..43891583
--- /dev/null
+++ b/src/utils/get-paginate/get-paginate.test.ts
@@ -0,0 +1,73 @@
+import { HookContext } from '@feathersjs/feathers';
+import { getPaginate } from './get-paginate';
+
+describe('getPaginate', () => {
+ it('returns service.options.paginate', function () {
+ const serviceOptions = {
+ paginate: {
+ default: 10,
+ max: 50,
+ },
+ };
+
+ const paginate = getPaginate({
+ params: {},
+ service: {
+ options: serviceOptions,
+ },
+ } as HookContext);
+
+ assert.deepStrictEqual(paginate, { default: 10, max: 50 });
+ });
+
+ it('returns undefined for params.paginate: false', function () {
+ const serviceOptions = {
+ paginate: {
+ default: 10,
+ max: 50,
+ },
+ };
+
+ const paginate = getPaginate({
+ params: { paginate: false },
+ service: {
+ options: serviceOptions,
+ },
+ } as HookContext);
+
+ assert.deepStrictEqual(paginate, undefined);
+ });
+
+ it('returns context.adapter.paginate over service.options.paginate', function () {
+ const serviceOptions = {
+ paginate: {
+ default: 10,
+ max: 50,
+ },
+ };
+
+ const paginate = getPaginate({
+ params: { adapter: { paginate: { default: 20, max: 100 } } },
+ service: {
+ options: serviceOptions,
+ },
+ } as HookContext);
+
+ assert.deepStrictEqual(paginate, { default: 20, max: 100 });
+ });
+
+ it('returns undefined for no paginate', function () {
+ const serviceOptions = {
+ paginate: false,
+ };
+
+ const paginate = getPaginate({
+ params: {},
+ service: {
+ options: serviceOptions,
+ },
+ } as HookContext);
+
+ assert.deepStrictEqual(paginate, undefined);
+ });
+});
diff --git a/src/utils/get-paginate/get-paginate.ts b/src/utils/get-paginate/get-paginate.ts
new file mode 100644
index 00000000..6e59fdc4
--- /dev/null
+++ b/src/utils/get-paginate/get-paginate.ts
@@ -0,0 +1,29 @@
+import type { PaginationOptions } from '@feathersjs/adapter-commons';
+import type { HookContext } from '@feathersjs/feathers';
+import { hasOwnProperty } from '../../internal.utils';
+
+/**
+ * util to get paginate options from context
+ * 1. it uses `context.params.paginate` if it exists
+ * 2. it uses `service.options.paginate` if it exists
+ * 3. it uses `context.params.adapter` if it exists
+ */
+export const getPaginate = (
+ context: H,
+): PaginationOptions | undefined => {
+ if (hasOwnProperty(context.params, 'paginate')) {
+ return (context.params.paginate as PaginationOptions) || undefined;
+ }
+
+ if (context.params.paginate === false) {
+ return undefined;
+ }
+ let options = context.service?.options || {};
+
+ options = {
+ ...options,
+ ...context.params.adapter,
+ };
+
+ return options.paginate || undefined;
+};
diff --git a/src/utils/get-result-is-array/get-result-is-array.test.ts b/src/utils/get-result-is-array/get-result-is-array.test.ts
new file mode 100644
index 00000000..01cf0c7d
--- /dev/null
+++ b/src/utils/get-result-is-array/get-result-is-array.test.ts
@@ -0,0 +1,139 @@
+import { getResultIsArray } from './get-result-is-array';
+
+describe('getResultIsArray', () => {
+ it('falsy result', () => {
+ expect(getResultIsArray({ result: null } as any)).toEqual({
+ isArray: false,
+ result: [],
+ key: 'result',
+ });
+
+ expect(getResultIsArray({} as any)).toEqual({
+ isArray: false,
+ result: [],
+ key: 'result',
+ });
+ });
+
+ it('array result', () => {
+ const result = [1, 2, 3];
+ expect(getResultIsArray({ result } as any)).toEqual({
+ isArray: true,
+ result,
+ key: 'result',
+ });
+
+ expect(getResultIsArray({ method: 'find', result } as any)).toEqual({
+ isArray: true,
+ result,
+ key: 'result',
+ });
+
+ expect(getResultIsArray({ method: 'find', result: { data: result } } as any)).toEqual({
+ isArray: true,
+ result,
+ key: 'result',
+ });
+ });
+
+ it('non-array result', () => {
+ const result = { a: 1, b: 2 };
+ expect(getResultIsArray({ result } as any)).toEqual({
+ isArray: false,
+ result: [result],
+ key: 'result',
+ });
+
+ expect(getResultIsArray({ method: 'find', result } as any)).toEqual({
+ isArray: false,
+ result: [result],
+ key: 'result',
+ });
+
+ expect(getResultIsArray({ method: 'find', result: { data: result } } as any)).toEqual({
+ isArray: false,
+ result: [result],
+ key: 'result',
+ });
+ });
+
+ it('dispatch', () => {
+ const result = { a: 1, b: 2 };
+ const dispatch = { c: 3, d: 4 };
+ expect(getResultIsArray({ result, dispatch } as any, { dispatch: true })).toEqual({
+ isArray: false,
+ result: [dispatch],
+ key: 'dispatch',
+ });
+
+ expect(
+ getResultIsArray({ method: 'find', result, dispatch } as any, { dispatch: true }),
+ ).toEqual({
+ isArray: false,
+ result: [dispatch],
+ key: 'dispatch',
+ });
+
+ expect(
+ getResultIsArray({ method: 'find', result: { data: result }, dispatch } as any, {
+ dispatch: true,
+ }),
+ ).toEqual({
+ isArray: false,
+ result: [dispatch],
+ key: 'dispatch',
+ });
+ });
+
+ it('returns dispatch if is missing with single result', () => {
+ const result = { a: 1, b: 2 };
+ const context = { result } as any;
+ expect(getResultIsArray(context, { dispatch: true })).toEqual({
+ isArray: false,
+ result: [result],
+ key: 'result',
+ });
+ expect(context.dispatch).toEqual(undefined);
+ });
+
+ it("doesn't set dispatch if is missing with array result", () => {
+ const result = [{ a: 1, b: 2 }];
+ const context = { method: 'create', result } as any;
+
+ expect(getResultIsArray(context, { dispatch: true })).toEqual({
+ isArray: true,
+ result: result,
+ key: 'result',
+ });
+ expect(context.dispatch).toEqual(undefined);
+ });
+
+ it("doesn't set dispatch if is missing with find array", () => {
+ const result = [{ a: 1, b: 2 }];
+ const dispatch = undefined;
+ const context = { method: 'find', result, dispatch } as any;
+
+ expect(getResultIsArray(context, { dispatch: true })).toEqual({
+ isArray: true,
+ result,
+ key: 'result',
+ });
+ });
+
+ it('sets dispatch if is missing with paginated result', () => {
+ const result = [{ a: 1, b: 2 }];
+ const dispatch = undefined;
+ const context = { method: 'find', result: { data: result, total: 3 }, dispatch } as any;
+
+ expect(
+ getResultIsArray(context, {
+ dispatch: true,
+ }),
+ ).toEqual({
+ isArray: true,
+ result,
+ key: 'result',
+ });
+ expect(context.dispatch).toEqual(undefined);
+ });
+});
diff --git a/src/utils/get-result-is-array/get-result-is-array.ts b/src/utils/get-result-is-array/get-result-is-array.ts
new file mode 100644
index 00000000..1c2b261d
--- /dev/null
+++ b/src/utils/get-result-is-array/get-result-is-array.ts
@@ -0,0 +1,35 @@
+import { HookContext } from '@feathersjs/feathers';
+import copy from 'fast-copy';
+
+type GetResultIsArrayOptions = {
+ dispatch?: boolean;
+};
+
+export function getResultIsArray(
+ context: H,
+ options?: GetResultIsArrayOptions,
+): { isArray: boolean; result: any[]; key: 'dispatch' | 'result' } {
+ const { dispatch = false } = options || {};
+
+ const isDispatch: boolean = dispatch && context.dispatch !== undefined;
+
+ const result = dispatch ? (isDispatch ? context.dispatch : copy(context.result)) : context.result;
+
+ if (!result) {
+ return {
+ isArray: false,
+ result: [],
+ key: isDispatch ? 'dispatch' : 'result',
+ };
+ }
+
+ const items = context.method === 'find' ? result.data || result : result;
+
+ const isArray = Array.isArray(items);
+
+ return {
+ isArray,
+ result: isArray ? items : items ? [items] : [],
+ key: isDispatch ? 'dispatch' : 'result',
+ };
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
new file mode 100644
index 00000000..7d39e7f6
--- /dev/null
+++ b/src/utils/index.ts
@@ -0,0 +1,19 @@
+export * from './calling-params/calling-params';
+export * from './check-context/check-context';
+export * from './check-context-if';
+export * from './combine/combine';
+
+export * from './get-items/get-items';
+export * from './get-data-is-array/get-data-is-array';
+export * from './get-result-is-array/get-result-is-array';
+
+export * from './params-for-server/params-for-server';
+
+export * from './replace-items/replace-items';
+export * from './replace-items/replace-data';
+export * from './replace-items/replace-result';
+
+export * from './run-hook/run-hook';
+
+export * from './get-paginate/get-paginate';
+export * from './skip-result/skip-result';
diff --git a/src/utils/is-not.ts b/src/utils/is-not.ts
deleted file mode 100755
index fa6b359d..00000000
--- a/src/utils/is-not.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { MethodNotAllowed } from '@feathersjs/errors';
-import type { HookContext } from '@feathersjs/feathers';
-import { isPromise } from '../common';
-import type { PredicateFn } from '../types';
-
-/**
- * Negate a sync or async predicate function.
- *
- * @see https://hooks-common.feathersjs.com/utilities.html#isnot
- */
-export function isNot(
- predicate: boolean | PredicateFn,
-): PredicateFn {
- if (typeof predicate !== 'function') {
- throw new MethodNotAllowed('Expected function as param. (isNot)');
- }
-
- return (context: H) => {
- const result = predicate(context); // Should we pass a clone? (safety vs performance)
-
- if (!isPromise(result)) {
- return !result;
- }
-
- return result.then(result1 => !result1);
- };
-}
diff --git a/test/utils/params-for-server.test.ts b/src/utils/params-for-server/params-for-server.test.ts
similarity index 95%
rename from test/utils/params-for-server.test.ts
rename to src/utils/params-for-server/params-for-server.test.ts
index bec8f3b4..a7d82359 100755
--- a/test/utils/params-for-server.test.ts
+++ b/src/utils/params-for-server/params-for-server.test.ts
@@ -1,6 +1,6 @@
import { assert } from 'vitest';
-import { paramsForServer } from '../../src';
+import { paramsForServer } from './params-for-server';
describe('util paramsToServer', () => {
it('handles empty params', () => {
diff --git a/src/utils/params-for-server.ts b/src/utils/params-for-server/params-for-server.ts
similarity index 92%
rename from src/utils/params-for-server.ts
rename to src/utils/params-for-server/params-for-server.ts
index eeec4eb1..db86fdcb 100755
--- a/src/utils/params-for-server.ts
+++ b/src/utils/params-for-server/params-for-server.ts
@@ -4,6 +4,8 @@ import type { Params } from '@feathersjs/feathers';
* Pass an explicit context.params from client to server. Client-side. (Utility function.)
*
* @see https://hooks-common.feathersjs.com/utilities.html#paramsforserver
+ *
+ * @deprecated use the hook `paramsForServer2` instead
*/
export function paramsForServer(params?: Params, ...whitelist: string[]): Params {
const ifWhitelist = !!whitelist.length;
diff --git a/src/utils/replace-items/replace-data.ts b/src/utils/replace-items/replace-data.ts
new file mode 100644
index 00000000..881164e2
--- /dev/null
+++ b/src/utils/replace-items/replace-data.ts
@@ -0,0 +1,43 @@
+import { HookContext } from '@feathersjs/feathers';
+import { getDataIsArray } from '../get-data-is-array/get-data-is-array';
+import { isPromise } from '../../common';
+import { Promisable } from '../../internal.utils';
+
+export function replaceData(
+ context: H,
+ cb: (item: any) => any,
+): Promisable {
+ if (!context.data) {
+ return context;
+ }
+
+ const { data, isArray } = getDataIsArray(context);
+
+ if (!data.length) {
+ return context;
+ }
+
+ let hasPromises = false;
+
+ const results = data.map(item => {
+ const result = cb(item);
+
+ if (!hasPromises && isPromise(result)) {
+ hasPromises = true;
+ }
+
+ return result;
+ });
+
+ function replace(data: any) {
+ context.data = isArray ? data : data[0];
+
+ return context;
+ }
+
+ if (hasPromises) {
+ return Promise.all(results).then(replace);
+ } else {
+ return replace(results);
+ }
+}
diff --git a/src/utils/replace-items/replace-items.test.ts b/src/utils/replace-items/replace-items.test.ts
new file mode 100644
index 00000000..271850ef
--- /dev/null
+++ b/src/utils/replace-items/replace-items.test.ts
@@ -0,0 +1,132 @@
+import { assert } from 'vitest';
+import { replaceItems } from './replace-items';
+import { actOnDispatch } from '../../hooks';
+
+// Tests when context.params._actOn === 'dispatch' are in act-on.test.ts
+describe('replaceItems', () => {
+ let hookBefore: any;
+ let hookAfter: any;
+ let hookFindPaginate: any;
+ let hookFind: any;
+
+ beforeEach(() => {
+ hookBefore = { type: 'before', method: 'create', params: { provider: 'rest' } };
+ hookAfter = { type: 'after', method: 'create', params: { provider: 'rest' } };
+ hookFindPaginate = {
+ type: 'after',
+ method: 'find',
+ params: { provider: 'rest' },
+ result: {
+ total: 200,
+ data: [],
+ },
+ };
+ hookFind = {
+ type: 'after',
+ method: 'find',
+ params: { provider: 'rest' },
+ };
+ });
+
+ it('updates hook before::create item', () => {
+ replaceItems(hookBefore, { a: 1 });
+ assert.deepEqual(hookBefore.data, { a: 1 });
+ });
+
+ it('updates hook before::create items', () => {
+ replaceItems(hookBefore, [{ a: 1 }, { b: 2 }]);
+ assert.deepEqual(hookBefore.data, [{ a: 1 }, { b: 2 }]);
+ });
+
+ it('updates hook after::create item', () => {
+ replaceItems(hookAfter, { a: 1 });
+ assert.deepEqual(hookAfter.result, { a: 1 });
+ });
+
+ it('updates hook after::create items', () => {
+ replaceItems(hookAfter, [{ a: 1 }, { b: 2 }]);
+ assert.deepEqual(hookAfter.result, [{ a: 1 }, { b: 2 }]);
+ });
+
+ it('updates hook after::find item', () => {
+ replaceItems(hookFind, { a: 1 });
+ assert.deepEqual(hookFind.result, { a: 1 });
+ });
+
+ it('updates hook after::find items', () => {
+ replaceItems(hookFind, [{ a: 1 }, { b: 2 }]);
+ assert.deepEqual(hookFind.result, [{ a: 1 }, { b: 2 }]);
+ });
+
+ it('updates hook after::find item paginated NOTE THIS TEST NOTE THIS TEST', () => {
+ replaceItems(hookFindPaginate, { a: 1 });
+ assert.equal(hookFindPaginate.result.total, 200);
+ assert.deepEqual(hookFindPaginate.result.data, [{ a: 1 }]);
+ });
+
+ it('updates hook after::find items paginated', () => {
+ replaceItems(hookFindPaginate, [{ a: 1 }, { b: 2 }]);
+ assert.equal(hookFindPaginate.result.total, 200);
+ assert.deepEqual(hookFindPaginate.result.data, [{ a: 1 }, { b: 2 }]);
+ });
+});
+
+describe('replaceItems actOnDispatch', () => {
+ let hookBefore: any;
+ let hookAfter: any;
+ let hookFindPaginate: any;
+ let hookFind: any;
+
+ beforeEach(() => {
+ hookBefore = { type: 'before', method: 'create', params: { provider: 'rest' } };
+ hookAfter = { type: 'after', method: 'create', params: { provider: 'rest' } };
+ hookFindPaginate = {
+ type: 'after',
+ method: 'find',
+ params: { provider: 'rest' },
+ dispatch: {
+ total: 200,
+ data: [],
+ },
+ };
+ hookFind = {
+ type: 'after',
+ method: 'find',
+ params: { provider: 'rest' },
+ };
+ });
+
+ it('updates hook after::create item', async () => {
+ await actOnDispatch(() => replaceItems(hookAfter, { a: 1 }))(hookAfter);
+ assert.deepEqual(hookAfter.dispatch, { a: 1 });
+ });
+
+ it('updates hook after::create items', async () => {
+ await actOnDispatch(() => replaceItems(hookAfter, [{ a: 1 }, { b: 2 }]))(hookAfter);
+ assert.deepEqual(hookAfter.dispatch, [{ a: 1 }, { b: 2 }]);
+ });
+
+ it('updates hook after::find item', async () => {
+ await actOnDispatch(() => replaceItems(hookFind, { a: 1 }))(hookFind);
+ assert.deepEqual(hookFind.dispatch, { a: 1 });
+ });
+
+ it('updates hook after::find items', async () => {
+ await actOnDispatch(() => replaceItems(hookFind, [{ a: 1 }, { b: 2 }]))(hookFind);
+ assert.deepEqual(hookFind.dispatch, [{ a: 1 }, { b: 2 }]);
+ });
+
+ it('updates hook after::find item paginated NOTE THIS TEST NOTE THIS TEST', async () => {
+ await actOnDispatch(() => replaceItems(hookFindPaginate, { a: 1 }))(hookFindPaginate);
+ assert.equal(hookFindPaginate.dispatch.total, 200);
+ assert.deepEqual(hookFindPaginate.dispatch.data, [{ a: 1 }]);
+ });
+
+ it('updates hook after::find items paginated', async () => {
+ await actOnDispatch(() => replaceItems(hookFindPaginate, [{ a: 1 }, { b: 2 }]))(
+ hookFindPaginate,
+ );
+ assert.equal(hookFindPaginate.dispatch.total, 200);
+ assert.deepEqual(hookFindPaginate.dispatch.data, [{ a: 1 }, { b: 2 }]);
+ });
+});
diff --git a/src/utils/replace-items.ts b/src/utils/replace-items/replace-items.ts
similarity index 92%
rename from src/utils/replace-items.ts
rename to src/utils/replace-items/replace-items.ts
index de0e8e1e..548e2b2e 100755
--- a/src/utils/replace-items.ts
+++ b/src/utils/replace-items/replace-items.ts
@@ -3,9 +3,10 @@ import type { HookContext } from '@feathersjs/feathers';
/**
* Replace the records in context.data or context.result[.data]. (Utility function.)
* @see https://hooks-common.feathersjs.com/utilities.html#replaceitems
+ *
+ * @deprecated Use `replaceData` or `replaceResult` instead.
*/
export function replaceItems(context: H, items: any): void {
- // @ts-ignore
if (context.params && context.params._actOn === 'dispatch') {
if (context.method === 'find' && context.dispatch?.data) {
context.dispatch.data = Array.isArray(items) ? items : [items];
diff --git a/src/utils/replace-items/replace-result.test.ts b/src/utils/replace-items/replace-result.test.ts
new file mode 100644
index 00000000..30fbf894
--- /dev/null
+++ b/src/utils/replace-items/replace-result.test.ts
@@ -0,0 +1,169 @@
+import { expect } from 'vitest';
+import { replaceResult } from './replace-result';
+
+// Tests when context.params._actOn === 'dispatch' are in act-on.test.ts
+describe('replaceResult', () => {
+ it("replaces context.result on paginated 'find'", async () => {
+ const context = {
+ method: 'find',
+ result: { total: 2, data: [{ id: 1 }, { id: 2 }] },
+ };
+
+ await replaceResult(context as any, item => ({ id: item.id + 1 }));
+
+ expect(context.result).toStrictEqual({ total: 2, data: [{ id: 2 }, { id: 3 }] });
+ });
+
+ it("replaces context.result on array 'find'", async () => {
+ const context = {
+ method: 'find',
+ result: [{ id: 1 }, { id: 2 }],
+ };
+
+ await replaceResult(context as any, item => ({ id: item.id + 1 }));
+
+ expect(context.result).toStrictEqual([{ id: 2 }, { id: 3 }]);
+ });
+
+ it("replaces context.result on 'get'", async () => {
+ const context = {
+ method: 'find',
+ id: 1,
+ result: { id: 1 },
+ };
+
+ await replaceResult(context as any, item => ({ id: item.id + 1 }));
+
+ expect(context.result).toStrictEqual({ id: 2 });
+ });
+
+ it("replaces context.result on 'create'", async () => {
+ const context = {
+ method: 'create',
+ result: { id: 1 },
+ };
+
+ await replaceResult(context as any, item => ({ id: item.id + 1 }));
+
+ expect(context.result).toStrictEqual({ id: 2 });
+ });
+
+ it("replaces context.result on multi:'create'", async () => {
+ const context = {
+ method: 'create',
+ result: [{ id: 1 }, { id: 2 }],
+ };
+
+ await replaceResult(context as any, item => ({ id: item.id + 1 }));
+
+ expect(context.result).toStrictEqual([{ id: 2 }, { id: 3 }]);
+ });
+
+ it('replaces context.result on update', async () => {
+ const context = {
+ method: 'update',
+ id: 1,
+ result: { id: 1 },
+ };
+
+ await replaceResult(context as any, item => ({ id: item.id + 1 }));
+
+ expect(context.result).toStrictEqual({ id: 2 });
+ });
+
+ it('replaces context.result on patch', async () => {
+ const context = {
+ method: 'patch',
+ id: 1,
+ result: { id: 1 },
+ };
+
+ await replaceResult(context as any, item => ({ id: item.id + 1 }));
+
+ expect(context.result).toStrictEqual({ id: 2 });
+ });
+
+ it('replaces context.result on multi patch', async () => {
+ const context = {
+ method: 'patch',
+ id: null,
+ result: [{ id: 1 }, { id: 2 }],
+ };
+
+ await replaceResult(context as any, item => ({ id: item.id + 1 }));
+
+ expect(context.result).toStrictEqual([{ id: 2 }, { id: 3 }]);
+ });
+
+ it('replaces context.result on remove', async () => {
+ const context = {
+ method: 'remove',
+ id: 1,
+ result: { id: 1 },
+ };
+
+ await replaceResult(context as any, item => ({ id: item.id + 1 }));
+
+ expect(context.result).toStrictEqual({ id: 2 });
+ });
+
+ it('replaces context.result on multi remove', async () => {
+ const context = {
+ method: 'remove',
+ id: null,
+ result: [{ id: 1 }, { id: 2 }],
+ };
+
+ await replaceResult(context as any, item => ({ id: item.id + 1 }));
+
+ expect(context.result).toStrictEqual([{ id: 2 }, { id: 3 }]);
+ });
+
+ it("replaces context.dispatch on 'get'", async () => {
+ const context = {
+ method: 'find',
+ id: 1,
+ result: { result: true },
+ dispatch: { dispatch: true },
+ };
+
+ await replaceResult(context as any, item => ({ ...item, test: true }), { dispatch: true });
+
+ expect(context.result).toStrictEqual({ result: true });
+ expect(context.dispatch).toStrictEqual({ dispatch: true, test: true });
+ });
+
+ it("replaces both context.result & context.dispatch on 'get'", async () => {
+ const context = {
+ method: 'find',
+ id: 1,
+ result: { result: true },
+ dispatch: { dispatch: true },
+ };
+
+ await replaceResult(context as any, item => ({ ...item, test: true }), { dispatch: 'both' });
+
+ expect(context.result).toStrictEqual({ result: true, test: true });
+ expect(context.dispatch).toStrictEqual({ dispatch: true, test: true });
+ });
+
+ it("replaces context.dispatch even though it was not there before on 'get'", async () => {
+ const context = {
+ method: 'find',
+ id: 1,
+ result: { result: true },
+ } as any;
+
+ await replaceResult(
+ context as any,
+ item => {
+ item.test = true;
+ return item;
+ },
+ { dispatch: true },
+ );
+
+ expect(context.result).toStrictEqual({ result: true });
+ expect(context.dispatch).toStrictEqual({ result: true, test: true });
+ });
+});
diff --git a/src/utils/replace-items/replace-result.ts b/src/utils/replace-items/replace-result.ts
new file mode 100644
index 00000000..2c4478e0
--- /dev/null
+++ b/src/utils/replace-items/replace-result.ts
@@ -0,0 +1,74 @@
+import { HookContext, NextFunction } from '@feathersjs/feathers';
+import { getResultIsArray } from '../get-result-is-array/get-result-is-array';
+import { isPromise } from '../../common';
+import copy from 'fast-copy';
+import { DispatchOption } from '../../types';
+
+export type ReplaceResultOptions = {
+ next?: NextFunction;
+ transform?: (items: any[]) => any[];
+ dispatch?: DispatchOption;
+};
+
+export async function replaceResult(
+ context: H,
+ cb: (item: any) => any,
+ options?: ReplaceResultOptions,
+): Promise {
+ if (options?.next) {
+ await options.next();
+ }
+
+ if (!!options?.dispatch && !context.dispatch) {
+ context.dispatch = copy(context.result);
+ }
+
+ async function forResult(dispatch: boolean) {
+ const { result, isArray, key } = getResultIsArray(context, { dispatch });
+
+ if (!result.length) {
+ return context;
+ }
+
+ let hasPromises = false;
+
+ const results = result.map(item => {
+ const result = cb(item);
+
+ if (!hasPromises && isPromise(result)) {
+ hasPromises = true;
+ }
+
+ return result;
+ });
+
+ function replace(r: any) {
+ if (options?.transform) {
+ r = options.transform(r);
+ }
+
+ if (!isArray) {
+ context[key] = r[0];
+ } else if (isArray && !Array.isArray(context[key]) && context[key].data) {
+ context[key].data = r;
+ } else {
+ context[key] = r;
+ }
+
+ return context;
+ }
+
+ if (hasPromises) {
+ return await Promise.all(results).then(replace);
+ } else {
+ return replace(results);
+ }
+ }
+
+ if (options?.dispatch === 'both') {
+ await Promise.all([forResult(true), forResult(false)]);
+ return context;
+ }
+
+ return await forResult(options?.dispatch ?? false);
+}
diff --git a/test/utils/run-hook.test.ts b/src/utils/run-hook/run-hook.test.ts
similarity index 97%
rename from test/utils/run-hook.test.ts
rename to src/utils/run-hook/run-hook.test.ts
index e92d9fe4..384d12b1 100755
--- a/test/utils/run-hook.test.ts
+++ b/src/utils/run-hook/run-hook.test.ts
@@ -1,5 +1,6 @@
import { assert } from 'vitest';
-import { fastJoin, keep, runHook } from '../../src';
+import { fastJoin, keep } from '../../hooks';
+import { runHook } from './run-hook';
const app = { a: 'a' };
const params = { p: 'p' };
diff --git a/src/utils/run-hook.ts b/src/utils/run-hook/run-hook.ts
similarity index 60%
rename from src/utils/run-hook.ts
rename to src/utils/run-hook/run-hook.ts
index 706b08f4..1ff4524a 100755
--- a/src/utils/run-hook.ts
+++ b/src/utils/run-hook/run-hook.ts
@@ -12,30 +12,27 @@ export function runHook(
return hookFunc => result => {
const ctx = Object.assign({}, { type: 'after', params: {}, result }, extraContent);
- // @ts-ignore
+ // @ts-expect-error TODO
if (typeof result === 'object' && result !== null && result.total && result.data) {
// @ts-expect-error method is readonly
ctx.method = 'find';
}
- return (
- Promise.resolve()
- // @ts-ignore
- .then(() => hookFunc(ctx))
- .then(newContext => {
- if (!newContext) {
- return;
- }
+ return Promise.resolve()
+ .then(() => hookFunc(ctx))
+ .then(newContext => {
+ if (!newContext) {
+ return;
+ }
- const result = newContext.result;
+ const result = newContext.result;
- if (typeof result === 'object' && result !== null && result.total && result.data) {
- // find
- return newContext.result;
- }
+ if (typeof result === 'object' && result !== null && result.total && result.data) {
+ // find
+ return newContext.result;
+ }
- return newContext.result.data || newContext.result;
- })
- );
+ return newContext.result.data || newContext.result;
+ });
};
}
diff --git a/src/utils/skip-result/skip-result.test.ts b/src/utils/skip-result/skip-result.test.ts
new file mode 100644
index 00000000..e209cc43
--- /dev/null
+++ b/src/utils/skip-result/skip-result.test.ts
@@ -0,0 +1,260 @@
+import type { HookContext } from '@feathersjs/feathers';
+import { skipResult } from './skip-result';
+
+describe('skipResult', function () {
+ const paginatedService = {
+ options: {
+ paginate: {
+ default: 10,
+ max: 50,
+ },
+ },
+ };
+
+ const nonPaginatedService = {
+ options: {
+ paginate: false,
+ },
+ };
+
+ const paramsEmpty = {};
+ const paramsPaginateFalse = { paginate: false };
+ const paramsPaginate = { paginate: { default: 10, max: 50 } };
+ const paramsAdapterPaginate = {
+ adapter: { paginate: { default: 10, max: 50 } },
+ };
+
+ it('does not overwrite result', function () {
+ ['find', 'get', 'create', 'update', 'patch', 'remove'].forEach(method => {
+ ['before', 'after'].forEach(type => {
+ [paginatedService, nonPaginatedService].forEach(service => {
+ [paramsPaginateFalse, paramsAdapterPaginate].forEach(params => {
+ const context = skipResult({
+ method,
+ type,
+ service,
+ params,
+ result: 123,
+ } as any as HookContext);
+
+ assert.deepStrictEqual(
+ context.result,
+ 123,
+ `result is not changed. '${type}:${method}': '${service}' - '${params}'`,
+ );
+ });
+ });
+ });
+ });
+ });
+
+ describe('find', function () {
+ it('sets paginated result', function () {
+ const combos = [
+ { service: paginatedService, params: paramsEmpty },
+ { service: paginatedService, params: paramsAdapterPaginate },
+ { service: nonPaginatedService, params: paramsPaginate },
+ { service: nonPaginatedService, params: paramsAdapterPaginate },
+ ];
+
+ combos.forEach(({ service, params }, i) => {
+ const { result } = skipResult({
+ service,
+ params,
+ method: 'find',
+ } as any as HookContext);
+ assert.deepStrictEqual(
+ result,
+ { total: 0, skip: 0, limit: 0, data: [] },
+ `'${i}': result is paginated empty`,
+ );
+ });
+ });
+
+ it('sets empty array', function () {
+ const combos = [
+ { service: paginatedService, params: paramsPaginateFalse },
+ { service: nonPaginatedService, params: paramsEmpty },
+ ];
+
+ combos.forEach(({ service, params }, i) => {
+ const { result } = skipResult({
+ service,
+ params,
+ method: 'find',
+ } as any as HookContext);
+ assert.deepStrictEqual(result, [], `'${i}': result is empty array`);
+ });
+ });
+ });
+
+ describe('get', function () {
+ it('sets result to null', function () {
+ const combos = [
+ { service: paginatedService, params: paramsEmpty },
+ { service: paginatedService, params: paramsAdapterPaginate },
+ { service: paginatedService, params: paramsPaginateFalse },
+ { service: nonPaginatedService, params: paramsPaginate },
+ { service: nonPaginatedService, params: paramsAdapterPaginate },
+ ];
+
+ combos.forEach(({ service, params }, i) => {
+ const { result } = skipResult({
+ service,
+ params,
+ method: 'get',
+ } as any as HookContext);
+ assert.deepStrictEqual(result, null, `'${i}': result is null`);
+ });
+ });
+ });
+
+ describe('create', function () {
+ it('sets result to null for single data', function () {
+ const combos = [
+ { service: paginatedService, params: paramsEmpty },
+ { service: paginatedService, params: paramsAdapterPaginate },
+ { service: paginatedService, params: paramsPaginateFalse },
+ { service: nonPaginatedService, params: paramsPaginate },
+ { service: nonPaginatedService, params: paramsAdapterPaginate },
+ ];
+
+ combos.forEach(({ service, params }, i) => {
+ const { result } = skipResult({
+ service,
+ params,
+ method: 'create',
+ data: { id: 1 },
+ } as any as HookContext);
+ assert.deepStrictEqual(result, null, `'${i}': result is null`);
+ });
+ });
+
+ it('sets result to empty array for array data', function () {
+ const combos = [
+ { service: paginatedService, params: paramsEmpty },
+ { service: paginatedService, params: paramsAdapterPaginate },
+ { service: paginatedService, params: paramsPaginateFalse },
+ { service: nonPaginatedService, params: paramsPaginate },
+ { service: nonPaginatedService, params: paramsAdapterPaginate },
+ ];
+
+ combos.forEach(({ service, params }, i) => {
+ const { result } = skipResult({
+ service,
+ params,
+ method: 'create',
+ data: [{ id: 1 }],
+ type: 'before',
+ } as any as HookContext);
+ assert.deepStrictEqual(result, [], `'${i}': result is empty array`);
+ });
+ });
+ });
+
+ describe('update', function () {
+ it('sets result to null', function () {
+ const combos = [
+ { service: paginatedService, params: paramsEmpty },
+ { service: paginatedService, params: paramsAdapterPaginate },
+ { service: paginatedService, params: paramsPaginateFalse },
+ { service: nonPaginatedService, params: paramsPaginate },
+ { service: nonPaginatedService, params: paramsAdapterPaginate },
+ ];
+
+ combos.forEach(({ service, params }, i) => {
+ const { result } = skipResult({
+ service,
+ params,
+ method: 'update',
+ id: 1,
+ } as any as HookContext);
+ assert.deepStrictEqual(result, null, `'${i}': result is null`);
+ });
+ });
+ });
+
+ describe('patch', function () {
+ it('sets result to null for id: 1', function () {
+ const combos = [
+ { service: paginatedService, params: paramsEmpty },
+ { service: paginatedService, params: paramsAdapterPaginate },
+ { service: paginatedService, params: paramsPaginateFalse },
+ { service: nonPaginatedService, params: paramsPaginate },
+ { service: nonPaginatedService, params: paramsAdapterPaginate },
+ ];
+
+ combos.forEach(({ service, params }, i) => {
+ const { result } = skipResult({
+ service,
+ params,
+ method: 'patch',
+ id: 1,
+ } as any as HookContext);
+ assert.deepStrictEqual(result, null, `'${i}': result is null`);
+ });
+ });
+
+ it('sets result to empty array for id: null', function () {
+ const combos = [
+ { service: paginatedService, params: paramsEmpty },
+ { service: paginatedService, params: paramsAdapterPaginate },
+ { service: paginatedService, params: paramsPaginateFalse },
+ { service: nonPaginatedService, params: paramsPaginate },
+ { service: nonPaginatedService, params: paramsAdapterPaginate },
+ ];
+
+ combos.forEach(({ service, params }, i) => {
+ const { result } = skipResult({
+ service,
+ params,
+ method: 'patch',
+ id: null,
+ } as any as HookContext);
+ assert.deepStrictEqual(result, [], `'${i}': result is empty array`);
+ });
+ });
+ });
+
+ describe('remove', function () {
+ it('sets result to null for id: 1', function () {
+ const combos = [
+ { service: paginatedService, params: paramsEmpty },
+ { service: paginatedService, params: paramsAdapterPaginate },
+ { service: paginatedService, params: paramsPaginateFalse },
+ { service: nonPaginatedService, params: paramsPaginate },
+ { service: nonPaginatedService, params: paramsAdapterPaginate },
+ ];
+
+ combos.forEach(({ service, params }, i) => {
+ const { result } = skipResult({
+ service,
+ params,
+ method: 'remove',
+ id: 1,
+ } as any as HookContext);
+ assert.deepStrictEqual(result, null, `'${i}': result is null`);
+ });
+ });
+
+ it('sets result to empty array for id: null', function () {
+ const combos = [
+ { service: paginatedService, params: paramsEmpty },
+ { service: paginatedService, params: paramsAdapterPaginate },
+ { service: paginatedService, params: paramsPaginateFalse },
+ { service: nonPaginatedService, params: paramsPaginate },
+ { service: nonPaginatedService, params: paramsAdapterPaginate },
+ ];
+
+ combos.forEach(({ service, params }, i) => {
+ const { result } = skipResult({
+ service,
+ params,
+ method: 'remove',
+ id: null,
+ } as any as HookContext);
+ assert.deepStrictEqual(result, [], `'${i}': result is empty array`);
+ });
+ });
+ });
+});
diff --git a/src/utils/skip-result/skip-result.ts b/src/utils/skip-result/skip-result.ts
new file mode 100644
index 00000000..afdc8565
--- /dev/null
+++ b/src/utils/skip-result/skip-result.ts
@@ -0,0 +1,30 @@
+import type { HookContext } from '@feathersjs/feathers';
+import { isMulti, isPaginated } from '../../predicates';
+
+/**
+ * util to set `context.result` to an empty array or object, depending on the hook type
+ */
+export const skipResult = (context: H) => {
+ if (context.result) {
+ return context;
+ }
+
+ const multi = isMulti(context);
+
+ if (multi) {
+ if (context.method === 'find' && isPaginated(context)) {
+ context.result = {
+ total: 0,
+ skip: 0,
+ limit: 0,
+ data: [],
+ };
+ } else {
+ context.result = [];
+ }
+ } else {
+ context.result = null;
+ }
+
+ return context;
+};
diff --git a/src/utils/some.ts b/src/utils/some.ts
deleted file mode 100755
index 7baf1506..00000000
--- a/src/utils/some.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import type { HookContext } from '@feathersjs/feathers';
-import type { AsyncPredicateFn, PredicateFn } from '../types';
-
-/**
- * Return the or of a series of sync or async predicate functions.
- * @see https://hooks-common.feathersjs.com/utilities.html#some
- */
-export function some(
- ...predicates: PredicateFn[]
-): AsyncPredicateFn {
- return async function (this: any, context: H) {
- const promises = predicates.map(fn => fn.apply(this, [context]));
-
- const results = await Promise.all(promises);
- return await Promise.resolve(results.some(result => !!result));
- };
-}
diff --git a/src/utils/transform-params/transform-params.ts b/src/utils/transform-params/transform-params.ts
new file mode 100644
index 00000000..17d17ebe
--- /dev/null
+++ b/src/utils/transform-params/transform-params.ts
@@ -0,0 +1,18 @@
+import { Params } from '@feathersjs/feathers';
+import { TransformParamsFn } from '../../types';
+
+/**
+ * Safely use a transformParams function to modify params.
+ */
+export const transformParams = (
+ params: P,
+ fn: TransformParamsFn
| undefined,
+): P => {
+ if (!fn) {
+ return params;
+ }
+
+ const result = fn({ ...params });
+
+ return result ?? params;
+};
diff --git a/test/hooks/get-replace-items.test.ts b/test/hooks/get-replace-items.test.ts
deleted file mode 100755
index e1105962..00000000
--- a/test/hooks/get-replace-items.test.ts
+++ /dev/null
@@ -1,241 +0,0 @@
-import { assert } from 'vitest';
-import { actOnDispatch, getItems, replaceItems } from '../../src';
-
-// Tests when context.params._actOn === 'dispatch' are in act-on.test.ts
-
-describe('services getItems & replaceItems', () => {
- let hookBefore: any;
- let hookAfter: any;
- let hookBeforeArray: any;
- let hookAfterArray: any;
- let hookFindPaginate: any;
- let hookFind: any;
-
- describe('getItems', () => {
- beforeEach(() => {
- hookBefore = {
- type: 'before',
- method: 'create',
- params: { provider: 'rest' },
- data: { first: 'John', last: 'Doe' },
- };
- hookBeforeArray = {
- type: 'before',
- method: 'create',
- params: { provider: 'rest' },
- data: [
- { first: 'John', last: 'Doe' },
- { first: 'Jane', last: 'Doe' },
- ],
- };
- hookAfter = {
- type: 'after',
- method: 'create',
- params: { provider: 'rest' },
- result: { first: 'Jane2', last: 'Doe2' },
- };
- hookAfterArray = {
- type: 'after',
- method: 'create',
- params: { provider: 'rest' },
- result: [
- { first: 'John2', last: 'Doe2' },
- { first: 'Jane', last: 'Doe' },
- ],
- };
- hookFindPaginate = {
- type: 'after',
- method: 'find',
- params: { provider: 'rest' },
- result: {
- total: 2,
- data: [
- { first: 'John3', last: 'Doe3' },
- { first: 'Jane3', last: 'Doe3' },
- ],
- },
- };
- hookFind = {
- type: 'after',
- method: 'find',
- params: { provider: 'rest' },
- result: [
- { first: 'John', last: 'Doe' },
- { first: 'Jane', last: 'Doe' },
- ],
- };
- });
-
- it('updates hook before::create item', () => {
- const stuff = getItems(hookBefore);
- assert.deepEqual(stuff, { first: 'John', last: 'Doe' });
- });
-
- it('updates hook before::create items', () => {
- const stuff = getItems(hookBeforeArray);
- assert.deepEqual(stuff, [
- { first: 'John', last: 'Doe' },
- { first: 'Jane', last: 'Doe' },
- ]);
- });
-
- it('updates hook after::create item', () => {
- const stuff = getItems(hookAfter);
- assert.deepEqual(stuff, { first: 'Jane2', last: 'Doe2' });
- });
-
- it('updates hook after::create items', () => {
- const stuff = getItems(hookAfterArray);
- assert.deepEqual(stuff, [
- { first: 'John2', last: 'Doe2' },
- { first: 'Jane', last: 'Doe' },
- ]);
- });
-
- it('updates hook after::find item', () => {
- const stuff = getItems(hookFindPaginate);
- assert.deepEqual(stuff, [
- { first: 'John3', last: 'Doe3' },
- { first: 'Jane3', last: 'Doe3' },
- ]);
- });
-
- it('updates hook after::find item paginated', () => {
- const stuff = getItems(hookFind);
- assert.deepEqual(stuff, [
- { first: 'John', last: 'Doe' },
- { first: 'Jane', last: 'Doe' },
- ]);
- });
-
- it('does not throw on before without data', () => {
- const hookBad: any = { type: 'before', method: 'create', params: { provider: 'rest' } };
- const stuff = getItems(hookBad);
- assert.equal(stuff, undefined);
- });
-
- it('does not throw on after without data', () => {
- const hookBad: any = { type: 'after', method: 'find', params: { provider: 'rest' } };
- const stuff = getItems(hookBad);
- assert.equal(stuff, undefined);
- });
- });
-
- describe('replaceItems', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', params: { provider: 'rest' } };
- hookAfter = { type: 'after', method: 'create', params: { provider: 'rest' } };
- hookFindPaginate = {
- type: 'after',
- method: 'find',
- params: { provider: 'rest' },
- result: {
- total: 200,
- data: [],
- },
- };
- hookFind = {
- type: 'after',
- method: 'find',
- params: { provider: 'rest' },
- };
- });
-
- it('updates hook before::create item', () => {
- replaceItems(hookBefore, { a: 1 });
- assert.deepEqual(hookBefore.data, { a: 1 });
- });
-
- it('updates hook before::create items', () => {
- replaceItems(hookBefore, [{ a: 1 }, { b: 2 }]);
- assert.deepEqual(hookBefore.data, [{ a: 1 }, { b: 2 }]);
- });
-
- it('updates hook after::create item', () => {
- replaceItems(hookAfter, { a: 1 });
- assert.deepEqual(hookAfter.result, { a: 1 });
- });
-
- it('updates hook after::create items', () => {
- replaceItems(hookAfter, [{ a: 1 }, { b: 2 }]);
- assert.deepEqual(hookAfter.result, [{ a: 1 }, { b: 2 }]);
- });
-
- it('updates hook after::find item', () => {
- replaceItems(hookFind, { a: 1 });
- assert.deepEqual(hookFind.result, { a: 1 });
- });
-
- it('updates hook after::find items', () => {
- replaceItems(hookFind, [{ a: 1 }, { b: 2 }]);
- assert.deepEqual(hookFind.result, [{ a: 1 }, { b: 2 }]);
- });
-
- it('updates hook after::find item paginated NOTE THIS TEST NOTE THIS TEST', () => {
- replaceItems(hookFindPaginate, { a: 1 });
- assert.equal(hookFindPaginate.result.total, 200);
- assert.deepEqual(hookFindPaginate.result.data, [{ a: 1 }]);
- });
-
- it('updates hook after::find items paginated', () => {
- replaceItems(hookFindPaginate, [{ a: 1 }, { b: 2 }]);
- assert.equal(hookFindPaginate.result.total, 200);
- assert.deepEqual(hookFindPaginate.result.data, [{ a: 1 }, { b: 2 }]);
- });
- });
-
- describe('replaceItems actOnDispatch', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', params: { provider: 'rest' } };
- hookAfter = { type: 'after', method: 'create', params: { provider: 'rest' } };
- hookFindPaginate = {
- type: 'after',
- method: 'find',
- params: { provider: 'rest' },
- dispatch: {
- total: 200,
- data: [],
- },
- };
- hookFind = {
- type: 'after',
- method: 'find',
- params: { provider: 'rest' },
- };
- });
-
- it('updates hook after::create item', async () => {
- await actOnDispatch(() => replaceItems(hookAfter, { a: 1 }))(hookAfter);
- assert.deepEqual(hookAfter.dispatch, { a: 1 });
- });
-
- it('updates hook after::create items', async () => {
- await actOnDispatch(() => replaceItems(hookAfter, [{ a: 1 }, { b: 2 }]))(hookAfter);
- assert.deepEqual(hookAfter.dispatch, [{ a: 1 }, { b: 2 }]);
- });
-
- it('updates hook after::find item', async () => {
- await actOnDispatch(() => replaceItems(hookFind, { a: 1 }))(hookFind);
- assert.deepEqual(hookFind.dispatch, { a: 1 });
- });
-
- it('updates hook after::find items', async () => {
- await actOnDispatch(() => replaceItems(hookFind, [{ a: 1 }, { b: 2 }]))(hookFind);
- assert.deepEqual(hookFind.dispatch, [{ a: 1 }, { b: 2 }]);
- });
-
- it('updates hook after::find item paginated NOTE THIS TEST NOTE THIS TEST', async () => {
- await actOnDispatch(() => replaceItems(hookFindPaginate, { a: 1 }))(hookFindPaginate);
- assert.equal(hookFindPaginate.dispatch.total, 200);
- assert.deepEqual(hookFindPaginate.dispatch.data, [{ a: 1 }]);
- });
-
- it('updates hook after::find items paginated', async () => {
- await actOnDispatch(() => replaceItems(hookFindPaginate, [{ a: 1 }, { b: 2 }]))(
- hookFindPaginate,
- );
- assert.equal(hookFindPaginate.dispatch.total, 200);
- assert.deepEqual(hookFindPaginate.dispatch.data, [{ a: 1 }, { b: 2 }]);
- });
- });
-});
diff --git a/test/hooks/iff-else.test.ts b/test/hooks/iff-else.test.ts
deleted file mode 100755
index b36757e4..00000000
--- a/test/hooks/iff-else.test.ts
+++ /dev/null
@@ -1,681 +0,0 @@
-import type { HookContext } from '@feathersjs/feathers';
-import { assert } from 'vitest';
-import { iff } from '../../src';
-import { isPromise } from '../../src/common';
-
-let hook: any;
-let hookBefore: any;
-let hookAfter: any;
-let hookFcnSyncCalls: any;
-let hookFcnAsyncCalls: any;
-let hookFcnCalls: any;
-let predicateHook: any;
-let predicateOptions: any;
-let predicateValue: any;
-
-const predicateSync = (hook: any) => {
- predicateHook = clone(hook);
- return true;
-};
-
-const predicateSync2 = (options: any) => (hook: any) => {
- predicateOptions = clone(options);
- predicateHook = clone(hook);
- return true;
-};
-
-const predicateAsync = (hook: any) => {
- predicateHook = clone(hook);
- return new Promise(resolve => resolve(true));
-};
-
-const predicateAsync2 = (options: any) => (hook: any) => {
- predicateOptions = clone(options);
- predicateHook = clone(hook);
- return new Promise(resolve => resolve(true));
-};
-
-const predicateAsyncFunny = (hook: any) => {
- predicateHook = clone(hook);
- return new Promise(resolve => {
- predicateValue = 'abc';
- return resolve(predicateValue);
- });
-};
-
-const hookFcnSync = (hook: HookContext): HookContext => {
- hookFcnSyncCalls += 1;
-
- hook.data.first = hook.data.first.toLowerCase();
-
- return hook;
-};
-
-const hookFcnAsync = (hook: HookContext) =>
- new Promise(resolve => {
- hookFcnAsyncCalls += 1;
-
- hook.data.first = hook.data.first.toLowerCase();
-
- resolve(hook);
- });
-
-const hookCb = (hook: any) => {
- hookFcnCalls += 1;
-
- return hook;
-};
-
-describe('services iff - sync predicate, sync hook', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- });
-
- it('calls sync hook function if truthy non-function', () => {
- return (
- iff(
- // @ts-ignore
- 'a',
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.deepEqual(hook, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-
- it('does not call sync hook function if falsey non-function', () => {
- // @ts-ignore
- const result: any = iff('', hookFcnSync)(hook);
-
- if (isPromise(result)) {
- assert.fail('promise unexpectedly returned');
- } else {
- assert.deepEqual(result, hookBefore);
- assert.equal(hookFcnSyncCalls, 0);
- assert.deepEqual(hook, hookBefore);
- }
- });
-
- it('calls sync hook function if sync predicate truthy', () => {
- return (
- iff(
- // @ts-ignore
- () => 'a',
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.deepEqual(hook, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-
- it('does not call sync hook function if sync predicate falsey', () => {
- // @ts-ignore
- const result = iff(() => '', hookFcnSync)(hook);
-
- if (isPromise(result)) {
- assert.fail('promise unexpectedly returned');
- } else {
- assert.deepEqual(result, hookBefore);
- assert.equal(hookFcnSyncCalls, 0);
- assert.deepEqual(hook, hookBefore);
- }
- });
-});
-
-describe('services iff - sync predicate, async hook', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- });
-
- it('calls async hook function if sync predicate truthy', () => {
- return (
- iff(
- true,
- hookFcnAsync,
- )(hook)
- // @ts-ignore
- .then((result1: any) => {
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-
- it('does not call async hook function if sync predicate falsey', () => {
- const result = iff(false, hookFcnAsync)(hook);
-
- if (isPromise(result)) {
- assert.fail('promise unexpectedly returned');
- } else {
- assert.deepEqual(result, hookBefore);
- assert.equal(hookFcnAsyncCalls, 0);
- assert.deepEqual(hook, hookBefore);
- }
- });
-
- it('calls async hook function if sync predicate returns truthy', () => {
- return (
- iff(
- () => true,
- hookFcnAsync,
- )(hook)
- // @ts-ignore
- .then((result1: any) => {
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-});
-
-describe('services iff - async predicate, sync hook', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- });
-
- it('calls sync hook function if aync predicate truthy', () => {
- return (
- iff(
- () => new Promise(resolve => resolve(true)),
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((result1: any) => {
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(result1, hookAfter);
- })
- );
- });
-
- it('does not call sync hook function if async predicate falsey', () => {
- return (
- iff(
- () => new Promise(resolve => resolve(false)),
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((result1: any) => {
- assert.deepEqual(result1, hookBefore);
- assert.equal(hookFcnSyncCalls, 0);
- assert.deepEqual(hook, hookBefore);
- })
- );
- });
-});
-
-describe('services iff - async predicate, async hook', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- });
-
- it('calls async hook function if aync predicate truthy', () => {
- return (
- iff(
- () => new Promise(resolve => resolve(true)),
- hookFcnAsync,
- )(hook)
- // @ts-ignore
- .then((result1: any) => {
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.deepEqual(result1, hookAfter);
- })
- );
- });
-
- it('does not call async hook function if async predicate falsey', () => {
- return (
- iff(
- () => new Promise(resolve => resolve(false)),
- hookFcnAsync,
- )(hook)
- // @ts-ignore
- .then((result1: any) => {
- assert.deepEqual(result1, hookBefore);
- assert.equal(hookFcnAsyncCalls, 0);
- assert.deepEqual(hook, hookBefore);
- })
- );
- });
-});
-
-describe('services iff - sync predicate', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- predicateHook = null;
- predicateOptions = null;
- });
-
- it('does not need to access hook', () => {
- return (
- iff(
- // @ts-ignore
- () => 'a',
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.deepEqual(hook, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-
- it('is passed hook as param', () => {
- return (
- iff(
- predicateSync,
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.deepEqual(predicateHook, hookBefore);
- assert.deepEqual(hook, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-
- it('a higher order predicate can pass more options', () => {
- return (
- iff(
- predicateSync2({ z: 'z' }),
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.deepEqual(predicateOptions, { z: 'z' });
- assert.deepEqual(predicateHook, hookBefore);
- assert.deepEqual(hook, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-});
-
-describe('services iff - async predicate', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- predicateHook = null;
- predicateOptions = null;
- predicateValue = null;
- });
-
- it('is passed hook as param', () => {
- return (
- iff(
- predicateAsync,
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((result1: any) => {
- assert.deepEqual(predicateHook, hookBefore);
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(result1, hookAfter);
- })
- );
- });
-
- it('is resolved', () => {
- return (
- iff(
- // @ts-ignore
- predicateAsyncFunny,
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((result1: any) => {
- assert.deepEqual(predicateHook, hookBefore);
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(result1, hookAfter);
-
- assert.equal(predicateValue, 'abc');
- })
- );
- });
-
- it('a higher order predicate can pass more options', () => {
- return (
- iff(
- predicateAsync2({ y: 'y' }),
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((result1: any) => {
- assert.deepEqual(predicateOptions, { y: 'y' });
- assert.deepEqual(predicateHook, hookBefore);
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(result1, hookAfter);
- })
- );
- });
-});
-
-describe('services iff - runs .else()', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- hookFcnCalls = 0;
- });
-
- it('using iff(true, ...)', () => {
- return (
- iff(
- true,
- hookFcnSync,
- hookFcnSync,
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.equal(hookFcnSyncCalls, 3);
- assert.equal(hookFcnAsyncCalls, 0);
- assert.equal(hookFcnCalls, 0);
-
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-
- it('using iff(true, ...).else(...)', () => {
- return (
- iff(true, hookFcnSync, hookFcnSync, hookFcnSync)
- .else(hookFcnSync)(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.equal(hookFcnSyncCalls, 3);
- assert.equal(hookFcnAsyncCalls, 0);
- assert.equal(hookFcnCalls, 0);
-
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-
- it('using if(false).else(...)', () => {
- return (
- iff(false, hookFcnSync)
- .else(
- hookFcnSync,
- hookFcnSync,
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.equal(hookFcnSyncCalls, 3);
- assert.equal(hookFcnAsyncCalls, 0);
- assert.equal(hookFcnCalls, 0);
-
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-});
-
-describe('services iff - runs iff(true, iff(true, ...)', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- hookFcnCalls = 0;
- });
-
- it('using iff(true, iff(true, hookFcnSync))', () => {
- return (
- iff(
- true,
- hookFcnAsync,
- iff(true, hookFcnSync),
- hookCb,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.equal(hookFcnSyncCalls, 1);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.equal(hookFcnCalls, 1);
-
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-
- it('using iff(true, iff(true, hookFcnAsync))', () => {
- return (
- iff(
- true,
- hookFcnSync,
- iff(true, hookFcnAsync),
- hookCb,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.equal(hookFcnSyncCalls, 1);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.equal(hookFcnCalls, 1);
-
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-
- it('runs iff(true, iff(true, hookFcnCb))', () => {
- return (
- iff(
- true,
- hookFcnSync,
- iff(true, hookCb),
- hookFcnAsync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.equal(hookFcnSyncCalls, 1);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.equal(hookFcnCalls, 1);
-
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-});
-
-describe('services iff - runs iff(true, iff(false).else(...)', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- hookFcnCalls = 0;
- });
-
- it('using iff(true, iff(false).else(hookFcnSync))', () => {
- return (
- iff(
- true,
- hookFcnAsync,
- iff(false, hookCb).else(hookFcnSync),
- hookFcnAsync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.equal(hookFcnSyncCalls, 1);
- assert.equal(hookFcnAsyncCalls, 2);
- assert.equal(hookFcnCalls, 0);
-
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-
- it('using iff(true, iff(false).else(hookFcnAsync))', () => {
- return (
- iff(
- true,
- hookFcnSync,
- iff(false, hookFcnSync).else(hookFcnAsync),
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.equal(hookFcnSyncCalls, 2);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.equal(hookFcnCalls, 0);
-
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-});
-
-describe('services iff - runs iff(false).else(iff(...).else(...))', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- hookFcnCalls = 0;
- });
-
- it('using iff(false).else(iff(true, ...))', () => {
- return (
- iff(false, hookCb)
- .else(
- hookFcnSync,
- iff(true, hookFcnAsync),
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.equal(hookFcnSyncCalls, 2);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.equal(hookFcnCalls, 0);
-
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-
- it('runs iff(false).else(iff(false).else(...))', () => {
- return (
- iff(false, hookCb)
- .else(
- hookFcnSync,
- iff(false, hookFcnSync).else(hookFcnAsync),
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.equal(hookFcnSyncCalls, 2);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.equal(hookFcnCalls, 0);
-
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-});
-
-describe('services iff - multiple iff() sequentially', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- hookFcnCalls = 0;
- });
-
- it('runs in iff(true, ...)', () => {
- return (
- iff(
- true,
- hookCb,
- iff(true, hookFcnSync, hookFcnSync, hookFcnSync),
- hookCb,
- iff(true, hookFcnAsync, hookFcnAsync, hookFcnAsync, hookFcnAsync),
- hookCb,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.equal(hookFcnSyncCalls, 3);
- assert.equal(hookFcnAsyncCalls, 4);
- assert.equal(hookFcnCalls, 3);
-
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-
- it('runs in iff(false).else(...)', () => {
- return (
- iff(false, hookCb)
- .else(
- hookFcnSync,
- iff(true, hookFcnAsync),
- iff(false, hookFcnSync).else(hookCb),
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.equal(hookFcnSyncCalls, 2);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.equal(hookFcnCalls, 1);
-
- assert.deepEqual(hook, hookAfter);
- })
- );
- });
-});
-
-// Helpers
-
-function clone(obj: any) {
- return JSON.parse(JSON.stringify(obj));
-}
diff --git a/test/hooks/sifter.test.ts b/test/hooks/sifter.test.ts
deleted file mode 100755
index d83abc2a..00000000
--- a/test/hooks/sifter.test.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { assert } from 'vitest';
-
-import sift from 'sift';
-import { sifter } from '../../src';
-
-const dataCanada = [
- { name: 'john', address: { city: 'montreal', country: 'canada' } },
- { name: 'david', address: { city: 'vancouver', country: 'canada' } },
-];
-
-const dataUsa = [{ name: 'marshall', address: { city: 'utah', country: 'usa' } }];
-
-const data = ([] as any[]).concat(dataCanada, dataUsa);
-
-const origHook = { type: 'after', method: 'find', result: data };
-
-const origHookPaginated = {
- type: 'after',
- method: 'find',
- result: { total: 1, limit: 10, skip: 0, data },
-};
-
-const getCountry = (country: any) => (_hook: any) => sift({ 'address.country': country });
-
-let hook: any;
-let hookPaginated: any;
-
-describe('services shifter', () => {
- beforeEach(() => {
- hook = clone(origHook);
- hookPaginated = clone(origHookPaginated);
- });
-
- it('sifts non-paginated data', () => {
- const hook1: any = sifter(getCountry('canada'))(hook);
- assert.deepEqual(hook1.result, dataCanada);
- });
-
- it('sifts paginated data', () => {
- const hook1: any = sifter(getCountry('canada'))(hookPaginated);
- assert.deepEqual(hook1.result.data, dataCanada);
- });
-
- it('throws if getSifter not a function', () => {
- // @ts-expect-error throws if getSifter not a function
- assert.throws(() => sifter({})(hookPaginated));
- });
-
- it('throws if getSifter does not return a function', () => {
- // @ts-expect-error throws if getSifter does not return a function
- assert.throws(() => sifter((hook: any) => ({}))(hookPaginated));
- });
-});
-
-// Helpers
-
-function clone(obj: any) {
- return JSON.parse(JSON.stringify(obj));
-}
diff --git a/test/hooks/stash-before.test.ts b/test/hooks/stash-before.test.ts
deleted file mode 100755
index e1c44372..00000000
--- a/test/hooks/stash-before.test.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-import { assert, expect } from 'vitest';
-import { feathers } from '@feathersjs/feathers';
-import { MemoryService } from '@feathersjs/memory';
-import { stashBefore } from '../../src';
-
-const startId = 6;
-const storeInit = {
- 0: { name: 'Jane Doe', key: 'a', id: 0 },
- 1: { name: 'Jack Doe', key: 'a', id: 1 },
- 2: { name: 'John Doe', key: 'a', id: 2 },
- 3: { name: 'Rick Doe', key: 'b', id: 3 },
- 4: { name: 'Dick Doe', key: 'b', id: 4 },
- 5: { name: 'Dork Doe', key: 'b', id: 5 },
-};
-
-let store;
-let finalParams: any;
-let innerCallParams: any;
-
-function services(this: any) {
- const app = this;
- app.configure(users);
-}
-
-function users(this: any) {
- const app = this;
- store = clone(storeInit);
-
- app.use(
- 'users',
- new MemoryService({
- store,
- startId,
- multi: true,
- }),
- );
-
- app.service('users').hooks({
- before: {
- all: [
- (context: any) => {
- if (context.params.disableStashBefore === true) {
- innerCallParams = context.params;
- }
- },
- stashBefore(),
- (context: any) => {
- finalParams = context.params;
- },
- ],
- },
- });
-}
-
-describe('services stash-before', () => {
- let app;
- let users: any;
-
- beforeEach(() => {
- innerCallParams = finalParams = null;
-
- app = feathers().configure(services);
-
- users = app.service('users');
- });
-
- ['get', 'update', 'patch', 'remove'].forEach(method => {
- it(`stashes on ${method}`, () => {
- return users[method](0, {}).then(() => {
- assert.deepEqual(finalParams.before, storeInit[0]);
- });
- });
- });
-
- it('Do not stash when query is used in remove', () => {
- return users.remove(null, { query: {} }).then(() => {
- assert.notProperty(finalParams, 'before');
- });
- });
-
- ['create', 'find'].forEach(method => {
- it(`throws on ${method}`, async () => {
- await expect(users[method]({})).rejects.toThrow();
- });
- });
-
- it('stashes on get with original params', () => {
- return users.get(0, { provider: 'socketio', eyecatcher: -1 }).then(() => {
- assert.equal(finalParams.provider, 'socketio');
- assert.equal(finalParams.eyecatcher, -1);
-
- assert.equal(innerCallParams.provider, 'socketio');
- assert.equal(innerCallParams.eyecatcher, -1);
- assert.notProperty(innerCallParams, 'authenticated');
- assert.notProperty(innerCallParams, 'user');
- });
- });
-
- it('stashes on patch with custom params', () => {
- return users.patch(0, {}, { provider: 'socketio', eyecatcher: -1 }).then(() => {
- assert.equal(finalParams.provider, 'socketio');
- assert.equal(finalParams.eyecatcher, -1);
-
- assert.equal(innerCallParams.provider, 'socketio');
- assert.notProperty(innerCallParams, 'eyecatcher');
- assert.property(innerCallParams, 'authenticated');
- assert.property(innerCallParams, 'user');
- });
- });
-});
-
-function clone(obj: any) {
- return JSON.parse(JSON.stringify(obj));
-}
diff --git a/test/hooks/when.test.ts b/test/hooks/when.test.ts
deleted file mode 100755
index 5cc51dd6..00000000
--- a/test/hooks/when.test.ts
+++ /dev/null
@@ -1,403 +0,0 @@
-import type { HookContext } from '@feathersjs/feathers';
-import { assert } from 'vitest';
-import { when } from '../../src';
-import { isPromise } from '../../src/common';
-
-let hook: any;
-let hookBefore: any;
-let hookAfter: any;
-let hookFcnSyncCalls: any;
-let hookFcnAsyncCalls: any;
-let hookFcnCalls: any;
-let predicateHook: any;
-let predicateOptions: any;
-let predicateValue: any;
-
-const predicateSync = (hook: any) => {
- predicateHook = clone(hook);
- return true;
-};
-
-const predicateSync2 = (options: any) => (hook: any) => {
- predicateOptions = clone(options);
- predicateHook = clone(hook);
- return true;
-};
-
-const predicateAsync = (hook: HookContext) => {
- predicateHook = clone(hook);
- return new Promise(resolve => resolve(true));
-};
-
-const predicateAsync2 = (options: any) => (hook: HookContext) => {
- predicateOptions = clone(options);
- predicateHook = clone(hook);
- return new Promise(resolve => resolve(true));
-};
-
-const predicateAsyncFunny = (hook: HookContext) => {
- predicateHook = clone(hook);
- return new Promise(resolve => {
- predicateValue = 'abc';
- return resolve(predicateValue);
- });
-};
-
-const hookFcnSync = (hook: HookContext) => {
- hookFcnSyncCalls = +1;
- hook.data.first = hook.data.first.toLowerCase();
-
- return hook;
-};
-
-const hookFcnAsync = (hook: HookContext) =>
- new Promise(resolve => {
- hookFcnAsyncCalls = +1;
- hook.data.first = hook.data.first.toLowerCase();
-
- resolve(hook);
- });
-
-const hookFcn = (hook: any) => {
- hookFcnCalls = +1;
-
- return hook;
-};
-
-describe('services when - sync predicate, sync hook', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- });
-
- it('calls sync hook function if truthy non-function', () => {
- when(
- // @ts-ignore
- 'a',
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.deepEqual(hook, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- });
- });
-
- it('does not call sync hook function if falsey non-function', () => {
- // @ts-ignore
- const result = when('', hookFcnSync)(hook);
-
- if (isPromise(result)) {
- assert.fail('promise unexpectedly returned');
- } else {
- assert.deepEqual(result, hookBefore);
- assert.equal(hookFcnSyncCalls, 0);
- assert.deepEqual(hook, hookBefore);
- }
- });
-
- it('calls sync hook function if sync predicate truthy', () => {
- when(
- // @ts-ignore
- () => 'a',
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.deepEqual(hook, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- });
- });
-
- it('does not call sync hook function if sync predicate falsey', () => {
- // @ts-ignore
- const result = when(() => '', hookFcnSync)(hook);
-
- if (isPromise(result)) {
- assert.fail('promise unexpectedly returned');
- } else {
- assert.deepEqual(result, hookBefore);
- assert.equal(hookFcnSyncCalls, 0);
- assert.deepEqual(hook, hookBefore);
- }
- });
-});
-
-describe('services when - sync predicate, async hook', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- });
-
- it('calls async hook function if sync predicate truthy', async () => {
- const result = when(true, hookFcnAsync)(hook);
-
- if (!isPromise(result)) {
- assert.fail('promise unexpectedly not returned');
- }
-
- await result.then((result1: any) => {
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- });
- });
-
- it('does not call async hook function if sync predicate falsey', () => {
- const result = when(false, hookFcnAsync)(hook);
-
- if (isPromise(result)) {
- assert.fail('promise unexpectedly returned');
- } else {
- assert.deepEqual(result, hookBefore);
- assert.equal(hookFcnAsyncCalls, 0);
- assert.deepEqual(hook, hookBefore);
- }
- });
-
- it('calls async hook function if sync predicate returns truthy', async () => {
- const result = when(() => true, hookFcnAsync)(hook);
-
- if (!isPromise(result)) {
- assert.fail('promise unexpectedly not returned');
- }
-
- await result.then((result1: any) => {
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- });
- });
-});
-
-describe('services when - async predicate, sync hook', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- });
-
- it('calls sync hook function if async predicate truthy', async () => {
- const result = when(() => new Promise(resolve => resolve(true)), hookFcnSync)(hook);
-
- if (!isPromise(result)) {
- assert.fail('promise unexpectedly not returned');
- }
-
- await result.then((result1: any) => {
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(result1, hookAfter);
- });
- });
-
- it('does not call sync hook function if async predicate falsey', async () => {
- const result = when(() => new Promise(resolve => resolve(false)), hookFcnSync)(hook);
-
- if (!isPromise(result)) {
- assert.fail('promise unexpectedly not returned');
- }
-
- await result.then((result1: any) => {
- assert.deepEqual(result1, hookBefore);
- assert.equal(hookFcnSyncCalls, 0);
- assert.deepEqual(hook, hookBefore);
- });
- });
-});
-
-describe('services when - async predicate, async hook', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- });
-
- it('calls async hook function if async predicate truthy', async () => {
- const result = when(() => new Promise(resolve => resolve(true)), hookFcnAsync)(hook);
-
- if (!isPromise(result)) {
- assert.fail('promise unexpectedly not returned');
- }
-
- await result.then((result1: any) => {
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.deepEqual(result1, hookAfter);
- });
- });
-
- it('does not call async hook function if async predicate falsey', async () => {
- const result = when(() => new Promise(resolve => resolve(false)), hookFcnAsync)(hook);
-
- if (!isPromise(result)) {
- assert.fail('promise unexpectedly not returned');
- }
-
- await result.then((result1: any) => {
- assert.deepEqual(result1, hookBefore);
- assert.equal(hookFcnAsyncCalls, 0);
- assert.deepEqual(hook, hookBefore);
- });
- });
-});
-
-describe('services when - sync predicate', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- predicateHook = null;
- predicateOptions = null;
- });
-
- it('does not need to access hook', () => {
- when(
- // @ts-ignore
- () => 'a',
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.deepEqual(hook, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- });
- });
-
- it('is passed hook as param', () => {
- when(
- predicateSync,
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.deepEqual(predicateHook, hookBefore);
- assert.deepEqual(hook, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- });
- });
-
- it('a higher order predicate can pass more options', () => {
- when(
- predicateSync2({ z: 'z' }),
- hookFcnSync,
- )(hook)
- // @ts-ignore
- .then((hook: any) => {
- assert.deepEqual(predicateOptions, { z: 'z' });
- assert.deepEqual(predicateHook, hookBefore);
- assert.deepEqual(hook, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(hook, hookAfter);
- });
- });
-});
-
-describe('services when - async predicate', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- predicateHook = null;
- predicateOptions = null;
- predicateValue = null;
- });
-
- it('is passed hook as param', async () => {
- // @ts-ignore
- const result = when(predicateAsync, hookFcnSync)(hook);
-
- if (!isPromise(result)) {
- assert.fail('promise unexpectedly not returned');
- }
-
- await result.then((result1: any) => {
- assert.deepEqual(predicateHook, hookBefore);
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(result1, hookAfter);
- });
- });
-
- it('is resolved', async () => {
- const result = when(predicateAsyncFunny, hookFcnSync)(hook);
-
- if (!isPromise(result)) {
- assert.fail('promise unexpectedly not returned');
- }
-
- await result.then((result1: any) => {
- assert.deepEqual(predicateHook, hookBefore);
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(result1, hookAfter);
-
- assert.equal(predicateValue, 'abc');
- });
- });
-
- it('a higher order predicate can pass more options', async () => {
- const result = when(predicateAsync2({ y: 'y' }), hookFcnSync)(hook);
-
- if (!isPromise(result)) {
- assert.fail('promise unexpectedly not returned');
- }
-
- await result.then((result1: any) => {
- assert.deepEqual(predicateOptions, { y: 'y' });
- assert.deepEqual(predicateHook, hookBefore);
- assert.deepEqual(result1, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.deepEqual(result1, hookAfter);
- });
- });
-});
-
-describe('services when - runs multiple hooks', () => {
- beforeEach(() => {
- hookBefore = { type: 'before', method: 'create', data: { first: 'John', last: 'Doe' } };
- hookAfter = { type: 'before', method: 'create', data: { first: 'john', last: 'Doe' } };
- hook = clone(hookBefore);
- hookFcnSyncCalls = 0;
- hookFcnAsyncCalls = 0;
- });
-
- it('runs successfully', async () => {
- await when(
- true,
- hookFcnSync,
- hookFcnAsync,
- hookFcn,
- )(hook).then((hook: any) => {
- assert.deepEqual(hook, hookAfter);
- assert.equal(hookFcnSyncCalls, 1);
- assert.equal(hookFcnAsyncCalls, 1);
- assert.equal(hookFcnCalls, 1);
- assert.deepEqual(hook, hookAfter);
- });
- });
-});
-
-// Helpers
-
-function clone(obj: any) {
- return JSON.parse(JSON.stringify(obj));
-}
diff --git a/test/index.test.ts b/test/index.test.ts
index c2908803..67d80b94 100755
--- a/test/index.test.ts
+++ b/test/index.test.ts
@@ -4,7 +4,12 @@ import * as allExported from '../src';
const members = [
'actOnDefault',
'actOnDispatch',
+
+ // alter
'alterItems',
+ 'alterData',
+ 'alterResult',
+
'cache',
'callingParams',
'callingParamsDefaults',
@@ -15,18 +20,45 @@ const members = [
'dePopulate',
'disablePagination',
'disallow',
+
+ // omit
'discard',
+ 'discardData',
+ 'discardResult',
'discardQuery',
+ 'omit',
+ 'omitData',
+ 'omitResult',
+ 'omitQuery',
+
'fastJoin',
'fgraphql',
'getItems',
+ 'getDataIsArray',
+ 'getResultIsArray',
'hookTypes',
'isProvider',
+
+ // pick
'keep',
- 'keepInArray',
+ 'keepData',
+ 'keepResult',
'keepQuery',
+ 'pick',
+ 'pickData',
+ 'pickResult',
+ 'pickQuery',
+
+ 'keepInArray',
+
'keepQueryInArray',
+
+ // lowercase
'lowerCase',
+ 'lowercase',
+ 'lowercaseData',
+ 'lowercaseResult',
+
'makeCallingParams',
'methodNames',
'mongoKeys',
@@ -34,14 +66,24 @@ const members = [
'paramsFromClient',
'populate',
'preventChanges',
+
+ // replace
'replaceItems',
+ 'replaceData',
+ 'replaceResult',
+
+ 'checkRequired',
'required',
'runHook',
'runParallel',
'sequelizeConvert',
'serialize',
'setField',
+
'setNow',
+ 'setNowData',
+ 'setNowResult',
+
'setSlug',
'sifter',
'softDelete',
@@ -49,17 +91,28 @@ const members = [
'traverse',
'validate',
'validateSchema',
+
+ // iff
'iffElse',
'iff',
'when',
'unless',
+
+ // predicates
'some',
'every',
'isNot',
+ 'not',
+ 'isMulti',
+ 'isPaginated',
+ 'isContext',
+
+ 'getPaginate',
+ 'skipResult',
].sort();
describe('services exposed hooks', () => {
it('no unexpected hooks', () => {
- assert.deepEqual(Object.keys(allExported).sort(), [].concat(members).sort());
+ assert.deepEqual(Object.keys(allExported).sort(), [...members].sort());
});
});
diff --git a/test/utils/check-context.test.ts b/test/utils/check-context.test.ts
deleted file mode 100755
index 7eac289c..00000000
--- a/test/utils/check-context.test.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import { assert } from 'vitest';
-
-import { checkContext } from '../../src';
-
-describe('util checkContext', () => {
- var hook: any; // eslint-disable-line no-var
-
- beforeEach(() => {
- hook = { type: 'before', method: 'create' };
- });
-
- it('handles "any" type and method', () => {
- assert.equal(checkContext(hook), undefined);
- });
-
- it('handles expected type', () => {
- hook.type = 'before';
- assert.equal(checkContext(hook, 'before'), undefined);
- });
-
- it('handles unexpected type', () => {
- hook.type = 'after';
- assert.throws(() => {
- checkContext(hook, 'before');
- });
- });
-
- it('handles undefined type', () => {
- hook.type = 'after';
- assert.equal(checkContext(hook), undefined);
- });
-
- it('handles null type', () => {
- hook.type = 'after';
- assert.equal(checkContext(hook, null), undefined);
- });
-
- it('handles expected type as array', () => {
- hook.type = 'before';
- assert.equal(checkContext(hook, ['before', 'after']), undefined);
- });
-
- it('handles unexpected type as array', () => {
- hook.type = 'error';
- assert.throws(() => {
- checkContext(hook, ['before', 'after']);
- });
- });
-
- it('handles expected method as string', () => {
- hook.method = 'create';
- assert.equal(checkContext(hook, null, 'create'), undefined);
- });
-
- it('handles unexpected method as string', () => {
- hook.method = 'patch';
- assert.throws(() => {
- checkContext(hook, null, 'create');
- });
- });
-
- it('handles expected method as array', () => {
- hook.method = 'create';
- assert.equal(checkContext(hook, null, ['create']), undefined);
- assert.equal(checkContext(hook, null, ['create', 'update', 'remove']), undefined);
- });
-
- it('handles unexpected method as array', () => {
- hook.method = 'patch';
- assert.throws(() => {
- checkContext(hook, null, ['create']);
- });
- assert.throws(() => {
- checkContext(hook, null, ['create', 'update', 'remove']);
- });
- });
-
- it('handles undefined method', () => {
- hook.method = 'patch';
- assert.equal(checkContext(hook, null), undefined);
- });
-
- it('handles null method', () => {
- hook.method = 'patch';
- assert.equal(checkContext(hook, null, null), undefined);
- });
-
- it('handles expected type and method as array', () => {
- hook.type = 'before';
- hook.method = 'create';
- assert.equal(checkContext(hook, 'before', ['create']), undefined);
- assert.equal(checkContext(hook, 'before', ['create', 'update', 'remove']), undefined);
- });
-
- it('allows custom methods', () => {
- hook.type = 'before';
- hook.method = 'custom';
- assert.equal(checkContext(hook, 'before', ['create']), undefined);
- assert.equal(checkContext(hook, 'before', ['create', 'update', 'remove']), undefined);
- });
-});
diff --git a/test/utils/some.test.ts b/test/utils/some.test.ts
deleted file mode 100755
index 5958a048..00000000
--- a/test/utils/some.test.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-import { assert } from 'vitest';
-import { feathers } from '@feathersjs/feathers';
-import { MemoryService } from '@feathersjs/memory';
-import { iff, some, isNot } from '../../src';
-
-describe('util some', () => {
- let app: any;
-
- beforeEach(() => {
- app = feathers().use('/users', new MemoryService());
- });
-
- describe('when at least 1 hook is truthy', () => {
- beforeEach(() => {
- app.service('users').hooks({
- before: {
- all: [
- iff(
- some(
- (_hook: any) => false,
- (_hook: any) => Promise.resolve(false),
- (_hook: any) => Promise.resolve(true),
- (_hook: any) => true,
- // @ts-ignore
- (_hook: any) => 1,
- (_hook: any) => {},
- ),
- (hook: any) => Promise.resolve(hook),
- ),
- ],
- },
- });
- });
-
- it('returns true', () => {
- return app
- .service('users')
- .find()
- .then((result: any) => {
- assert.deepEqual(result, []);
- });
- });
- });
-
- describe('when a hook throws an error', () => {
- beforeEach(() => {
- app.service('users').hooks({
- before: {
- all: [
- iff(
- some(
- (_hook: any) => true,
- (_hook: any) => {
- throw new Error('Hook 2 errored');
- },
- (_hook: any) => true,
- ),
- (hook: any) => Promise.resolve(hook),
- ),
- ],
- },
- });
- });
-
- it('rejects with the error', () => {
- return app
- .service('users')
- .find()
- .catch((error: any) => {
- assert.equal(error.message, 'Hook 2 errored');
- });
- });
- });
-
- describe('when a hook rejects with an error', () => {
- beforeEach(() => {
- app.service('users').hooks({
- before: {
- all: [
- iff(
- some(
- (_hook: any) => true,
- (_hook: any) => Promise.reject(Error('Hook 2 errored')),
- (_hook: any) => true,
- ),
- (hook: any) => Promise.resolve(hook),
- ),
- ],
- },
- });
- });
-
- it('rejects with the error', () => {
- return app
- .service('users')
- .find()
- .catch((error: any) => {
- assert.equal(error.message, 'Hook 2 errored');
- });
- });
- });
-
- describe('when all hooks are falsey', () => {
- beforeEach(() => {
- app.service('users').hooks({
- before: {
- all: [
- iff(
- isNot(
- some(
- (_hook: any) => false,
- (_hook: any) => Promise.resolve(false),
- // @ts-ignore
- (_hook: any) => null,
- (_hook: any) => undefined,
- // @ts-ignore
- (_hook: any) => 0,
- ),
- ),
- () => Promise.reject(new Error('All hooks returned false')),
- ),
- ],
- },
- });
- });
-
- it('returns false', () => {
- return app
- .service('users')
- .find()
- .catch((error: any) => {
- assert.equal(error.message, 'All hooks returned false');
- });
- });
- });
-});
diff --git a/vite.config.ts b/vite.config.ts
index 4ceeee18..439fd77b 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,19 +1,19 @@
-import { defineConfig } from "vitest/config";
+import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
coverage: {
- provider: "v8",
- reporter: ["text", "lcov"],
- include: ["src/**/*.{js,ts}"],
- exclude: ["**/*.test.{js,ts}", "src/types.ts"],
+ provider: 'v8',
+ reporter: ['text', 'lcov'],
+ include: ['src/**/*.{js,ts}'],
+ exclude: ['**/*.test.{js,ts}', 'src/types.ts'],
thresholds: {
lines: 85,
functions: 85,
branches: 85,
statements: 85,
- }
+ },
},
},
});