feathers-utils 4.1.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +39 -11
- package/dist/index.d.cts +16 -4
- package/dist/index.d.mts +16 -4
- package/dist/index.d.ts +16 -4
- package/dist/index.mjs +39 -12
- package/package.json +1 -1
- package/src/hooks/forEach.ts +37 -16
- package/src/utils/index.ts +1 -0
- package/src/utils/optimizeBatchPatch.ts +66 -0
package/dist/index.cjs
CHANGED
|
@@ -607,6 +607,30 @@ const validateQueryProperty = (query, operators = []) => {
|
|
|
607
607
|
};
|
|
608
608
|
};
|
|
609
609
|
|
|
610
|
+
function optimizeBatchPatch(items, options) {
|
|
611
|
+
const map = [];
|
|
612
|
+
const id = options?.id ?? "id";
|
|
613
|
+
for (const [id2, data] of items) {
|
|
614
|
+
const index = map.findIndex((item) => fastEquals.deepEqual(item.data, data));
|
|
615
|
+
if (index === -1) {
|
|
616
|
+
map.push({ ids: [id2], data });
|
|
617
|
+
} else {
|
|
618
|
+
map[index].ids.push(id2);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return map.map(({ ids, data }) => {
|
|
622
|
+
return ids.length === 1 ? [ids[0], data, void 0] : [
|
|
623
|
+
null,
|
|
624
|
+
data,
|
|
625
|
+
{
|
|
626
|
+
query: {
|
|
627
|
+
[id]: { $in: ids }
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
];
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
610
634
|
function checkMulti() {
|
|
611
635
|
return (context) => {
|
|
612
636
|
if (shouldSkip("checkMulti", context)) {
|
|
@@ -658,27 +682,30 @@ function createRelated({
|
|
|
658
682
|
};
|
|
659
683
|
}
|
|
660
684
|
|
|
661
|
-
const forEach = (actionPerItem,
|
|
662
|
-
const
|
|
663
|
-
wait: "parallel",
|
|
664
|
-
items: "automatic",
|
|
665
|
-
..._options
|
|
666
|
-
};
|
|
685
|
+
const forEach = (actionPerItem, options) => {
|
|
686
|
+
const { wait = "parallel", items: from = "automatic" } = options || {};
|
|
667
687
|
return async (context) => {
|
|
668
|
-
if (shouldSkip("
|
|
688
|
+
if (shouldSkip("forEach", context)) {
|
|
669
689
|
return context;
|
|
670
690
|
}
|
|
671
|
-
const { items } = getItemsIsArray(context, { from
|
|
691
|
+
const { items } = getItemsIsArray(context, { from });
|
|
692
|
+
const forAll = options?.forAll ? await options.forAll(items, context) : {};
|
|
672
693
|
const promises = [];
|
|
694
|
+
let i = 0;
|
|
673
695
|
for (const item of items) {
|
|
674
|
-
const promise = actionPerItem(item,
|
|
675
|
-
|
|
696
|
+
const promise = actionPerItem(item, {
|
|
697
|
+
context,
|
|
698
|
+
i,
|
|
699
|
+
fromAll: forAll
|
|
700
|
+
});
|
|
701
|
+
if (wait === "sequential") {
|
|
676
702
|
await promise;
|
|
677
703
|
} else {
|
|
678
704
|
promises.push(promise);
|
|
679
705
|
}
|
|
706
|
+
i++;
|
|
680
707
|
}
|
|
681
|
-
if (
|
|
708
|
+
if (wait === "parallel") {
|
|
682
709
|
await Promise.all(promises);
|
|
683
710
|
}
|
|
684
711
|
return context;
|
|
@@ -986,6 +1013,7 @@ exports.markHookForSkip = markHookForSkip;
|
|
|
986
1013
|
exports.mergeArrays = mergeArrays;
|
|
987
1014
|
exports.mergeQuery = mergeQuery;
|
|
988
1015
|
exports.onDelete = onDelete;
|
|
1016
|
+
exports.optimizeBatchPatch = optimizeBatchPatch;
|
|
989
1017
|
exports.parseFields = parseFields;
|
|
990
1018
|
exports.pushSet = pushSet;
|
|
991
1019
|
exports.removeRelated = removeRelated;
|
package/dist/index.d.cts
CHANGED
|
@@ -36,11 +36,17 @@ interface GetItemsIsArrayResult<T = any> {
|
|
|
36
36
|
}
|
|
37
37
|
declare const getItemsIsArray: <T = any, H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H, options?: GetItemsIsArrayOptions) => GetItemsIsArrayResult<T>;
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
type HookForEachOptions<T = any, H = HookContext, R = never> = {
|
|
40
40
|
wait?: "sequential" | "parallel" | false;
|
|
41
41
|
items?: GetItemsIsArrayOptions["from"];
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
forAll?: (items: T[], context: H) => Promisable<R>;
|
|
43
|
+
};
|
|
44
|
+
type ActionPerItem<T, H, R> = (item: T, options: {
|
|
45
|
+
context: H;
|
|
46
|
+
i: number;
|
|
47
|
+
fromAll: R;
|
|
48
|
+
}) => Promisable<any>;
|
|
49
|
+
declare const forEach: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any, R = never>(actionPerItem: ActionPerItem<T, H, R>, options?: HookForEachOptions<T, H, R>) => ReturnAsyncHook<H>;
|
|
44
50
|
|
|
45
51
|
type OnDeleteAction = "cascade" | "set null";
|
|
46
52
|
interface OnDeleteOptions {
|
|
@@ -234,6 +240,12 @@ declare const toJSON: (context: HookContext) => HookContext<_feathersjs_feathers
|
|
|
234
240
|
*/
|
|
235
241
|
declare const validateQueryProperty: (query: any, operators?: string[]) => Query;
|
|
236
242
|
|
|
243
|
+
type OptimizeBatchPatchOptions = {
|
|
244
|
+
id?: string;
|
|
245
|
+
};
|
|
246
|
+
type OptimizeBatchPatchResultItem<T = Record<string, unknown>, P = Params> = [Id, T, P | undefined];
|
|
247
|
+
declare function optimizeBatchPatch<T extends Record<string, unknown>, P extends Params>(items: Map<Id, T>, options?: OptimizeBatchPatchOptions): OptimizeBatchPatchResultItem<T, P>[];
|
|
248
|
+
|
|
237
249
|
declare const filterArray: <T extends string[]>(...keys: T) => { [key in T[number]]: (value: any, options: FilterQueryOptions) => any; };
|
|
238
250
|
|
|
239
251
|
declare const filterObject: <T extends string[]>(...keys: T) => { [key in T[number]]: (value: any, options: FilterQueryOptions) => any; };
|
|
@@ -284,4 +296,4 @@ type InferRemoveResultFromPath<App extends Application, Path extends string, IdO
|
|
|
284
296
|
type InferDataFromPath<App extends Application, Path extends string, Method extends "create" | "update" | "patch"> = Method extends "create" ? InferCreateDataFromPath<App, Path> : Method extends "update" ? InferUpdateDataFromPath<App, Path> : Method extends "patch" ? InferPatchDataFromPath<App, Path> : never;
|
|
285
297
|
type InferResultFromPath<App extends Application, Path extends string, Method extends "get" | "find" | "create" | "update" | "patch" | "remove"> = Method extends "get" ? InferGetResultFromPath<App, Path> : Method extends "find" ? InferFindResultFromPath<App, Path> : Method extends "create" ? InferCreateResultFromPath<App, Path> : Method extends "update" ? InferUpdateResultFromPath<App, Path> : Method extends "patch" ? InferPatchResultFromPath<App, Path> : Method extends "remove" ? InferRemoveResultFromPath<App, Path> : never;
|
|
286
298
|
|
|
287
|
-
export { type ActionOnEmptyIntersect, type CreateRelatedOptions, type DebouncedFunctionApp, type DebouncedService, DebouncedStore, type DebouncedStoreOptions, type FirstLast, type GetItemsIsArrayFrom, type GetItemsIsArrayOptions, type GetItemsIsArrayResult, type GetService, type Handle, type HookForEachOptions, type HookRunPerItemOptions, type HookSetDataOptions, type InferCreateData, type InferCreateDataFromPath, type InferCreateDataSingle, type InferCreateDataSingleFromPath, type InferCreateResult, type InferCreateResultFromPath, type InferCreateResultSingle, type InferCreateResultSingleFromPath, type InferDataFromPath, type InferFindResult, type InferFindResultFromPath, type InferGetResult, type InferGetResultFromPath, type InferPatchData, type InferPatchDataFromPath, type InferPatchResult, type InferPatchResultFromPath, type InferRemoveResult, type InferRemoveResultFromPath, type InferResultFromPath, type InferUpdateData, type InferUpdateDataFromPath, type InferUpdateResult, type InferUpdateResultFromPath, type InitDebounceMixinOptions, type MergeQueryOptions, type OnDeleteAction, type OnDeleteOptions, type Predicate, type PredicateWithContext, type PushSetOptions, type RemoveRelatedOptions, type SetQueryKeySafelyOptions, type ShouldSkipOptions, checkMulti, createRelated, debounceMixin, defineHooks, filterArray, filterObject, filterQuery, forEach, getItemsIsArray, getPaginate, isMulti, isPaginated, makeDefaultOptions, markHookForSkip, mergeArrays, mergeQuery, onDelete, parseFields, pushSet, removeRelated, runPerItem, setData, setQueryKeySafely, setResultEmpty, shouldSkip, toJSON, validateQueryProperty };
|
|
299
|
+
export { type ActionOnEmptyIntersect, type CreateRelatedOptions, type DebouncedFunctionApp, type DebouncedService, DebouncedStore, type DebouncedStoreOptions, type FirstLast, type GetItemsIsArrayFrom, type GetItemsIsArrayOptions, type GetItemsIsArrayResult, type GetService, type Handle, type HookForEachOptions, type HookRunPerItemOptions, type HookSetDataOptions, type InferCreateData, type InferCreateDataFromPath, type InferCreateDataSingle, type InferCreateDataSingleFromPath, type InferCreateResult, type InferCreateResultFromPath, type InferCreateResultSingle, type InferCreateResultSingleFromPath, type InferDataFromPath, type InferFindResult, type InferFindResultFromPath, type InferGetResult, type InferGetResultFromPath, type InferPatchData, type InferPatchDataFromPath, type InferPatchResult, type InferPatchResultFromPath, type InferRemoveResult, type InferRemoveResultFromPath, type InferResultFromPath, type InferUpdateData, type InferUpdateDataFromPath, type InferUpdateResult, type InferUpdateResultFromPath, type InitDebounceMixinOptions, type MergeQueryOptions, type OnDeleteAction, type OnDeleteOptions, type OptimizeBatchPatchOptions, type OptimizeBatchPatchResultItem, type Predicate, type PredicateWithContext, type PushSetOptions, type RemoveRelatedOptions, type SetQueryKeySafelyOptions, type ShouldSkipOptions, checkMulti, createRelated, debounceMixin, defineHooks, filterArray, filterObject, filterQuery, forEach, getItemsIsArray, getPaginate, isMulti, isPaginated, makeDefaultOptions, markHookForSkip, mergeArrays, mergeQuery, onDelete, optimizeBatchPatch, parseFields, pushSet, removeRelated, runPerItem, setData, setQueryKeySafely, setResultEmpty, shouldSkip, toJSON, validateQueryProperty };
|
package/dist/index.d.mts
CHANGED
|
@@ -36,11 +36,17 @@ interface GetItemsIsArrayResult<T = any> {
|
|
|
36
36
|
}
|
|
37
37
|
declare const getItemsIsArray: <T = any, H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H, options?: GetItemsIsArrayOptions) => GetItemsIsArrayResult<T>;
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
type HookForEachOptions<T = any, H = HookContext, R = never> = {
|
|
40
40
|
wait?: "sequential" | "parallel" | false;
|
|
41
41
|
items?: GetItemsIsArrayOptions["from"];
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
forAll?: (items: T[], context: H) => Promisable<R>;
|
|
43
|
+
};
|
|
44
|
+
type ActionPerItem<T, H, R> = (item: T, options: {
|
|
45
|
+
context: H;
|
|
46
|
+
i: number;
|
|
47
|
+
fromAll: R;
|
|
48
|
+
}) => Promisable<any>;
|
|
49
|
+
declare const forEach: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any, R = never>(actionPerItem: ActionPerItem<T, H, R>, options?: HookForEachOptions<T, H, R>) => ReturnAsyncHook<H>;
|
|
44
50
|
|
|
45
51
|
type OnDeleteAction = "cascade" | "set null";
|
|
46
52
|
interface OnDeleteOptions {
|
|
@@ -234,6 +240,12 @@ declare const toJSON: (context: HookContext) => HookContext<_feathersjs_feathers
|
|
|
234
240
|
*/
|
|
235
241
|
declare const validateQueryProperty: (query: any, operators?: string[]) => Query;
|
|
236
242
|
|
|
243
|
+
type OptimizeBatchPatchOptions = {
|
|
244
|
+
id?: string;
|
|
245
|
+
};
|
|
246
|
+
type OptimizeBatchPatchResultItem<T = Record<string, unknown>, P = Params> = [Id, T, P | undefined];
|
|
247
|
+
declare function optimizeBatchPatch<T extends Record<string, unknown>, P extends Params>(items: Map<Id, T>, options?: OptimizeBatchPatchOptions): OptimizeBatchPatchResultItem<T, P>[];
|
|
248
|
+
|
|
237
249
|
declare const filterArray: <T extends string[]>(...keys: T) => { [key in T[number]]: (value: any, options: FilterQueryOptions) => any; };
|
|
238
250
|
|
|
239
251
|
declare const filterObject: <T extends string[]>(...keys: T) => { [key in T[number]]: (value: any, options: FilterQueryOptions) => any; };
|
|
@@ -284,4 +296,4 @@ type InferRemoveResultFromPath<App extends Application, Path extends string, IdO
|
|
|
284
296
|
type InferDataFromPath<App extends Application, Path extends string, Method extends "create" | "update" | "patch"> = Method extends "create" ? InferCreateDataFromPath<App, Path> : Method extends "update" ? InferUpdateDataFromPath<App, Path> : Method extends "patch" ? InferPatchDataFromPath<App, Path> : never;
|
|
285
297
|
type InferResultFromPath<App extends Application, Path extends string, Method extends "get" | "find" | "create" | "update" | "patch" | "remove"> = Method extends "get" ? InferGetResultFromPath<App, Path> : Method extends "find" ? InferFindResultFromPath<App, Path> : Method extends "create" ? InferCreateResultFromPath<App, Path> : Method extends "update" ? InferUpdateResultFromPath<App, Path> : Method extends "patch" ? InferPatchResultFromPath<App, Path> : Method extends "remove" ? InferRemoveResultFromPath<App, Path> : never;
|
|
286
298
|
|
|
287
|
-
export { type ActionOnEmptyIntersect, type CreateRelatedOptions, type DebouncedFunctionApp, type DebouncedService, DebouncedStore, type DebouncedStoreOptions, type FirstLast, type GetItemsIsArrayFrom, type GetItemsIsArrayOptions, type GetItemsIsArrayResult, type GetService, type Handle, type HookForEachOptions, type HookRunPerItemOptions, type HookSetDataOptions, type InferCreateData, type InferCreateDataFromPath, type InferCreateDataSingle, type InferCreateDataSingleFromPath, type InferCreateResult, type InferCreateResultFromPath, type InferCreateResultSingle, type InferCreateResultSingleFromPath, type InferDataFromPath, type InferFindResult, type InferFindResultFromPath, type InferGetResult, type InferGetResultFromPath, type InferPatchData, type InferPatchDataFromPath, type InferPatchResult, type InferPatchResultFromPath, type InferRemoveResult, type InferRemoveResultFromPath, type InferResultFromPath, type InferUpdateData, type InferUpdateDataFromPath, type InferUpdateResult, type InferUpdateResultFromPath, type InitDebounceMixinOptions, type MergeQueryOptions, type OnDeleteAction, type OnDeleteOptions, type Predicate, type PredicateWithContext, type PushSetOptions, type RemoveRelatedOptions, type SetQueryKeySafelyOptions, type ShouldSkipOptions, checkMulti, createRelated, debounceMixin, defineHooks, filterArray, filterObject, filterQuery, forEach, getItemsIsArray, getPaginate, isMulti, isPaginated, makeDefaultOptions, markHookForSkip, mergeArrays, mergeQuery, onDelete, parseFields, pushSet, removeRelated, runPerItem, setData, setQueryKeySafely, setResultEmpty, shouldSkip, toJSON, validateQueryProperty };
|
|
299
|
+
export { type ActionOnEmptyIntersect, type CreateRelatedOptions, type DebouncedFunctionApp, type DebouncedService, DebouncedStore, type DebouncedStoreOptions, type FirstLast, type GetItemsIsArrayFrom, type GetItemsIsArrayOptions, type GetItemsIsArrayResult, type GetService, type Handle, type HookForEachOptions, type HookRunPerItemOptions, type HookSetDataOptions, type InferCreateData, type InferCreateDataFromPath, type InferCreateDataSingle, type InferCreateDataSingleFromPath, type InferCreateResult, type InferCreateResultFromPath, type InferCreateResultSingle, type InferCreateResultSingleFromPath, type InferDataFromPath, type InferFindResult, type InferFindResultFromPath, type InferGetResult, type InferGetResultFromPath, type InferPatchData, type InferPatchDataFromPath, type InferPatchResult, type InferPatchResultFromPath, type InferRemoveResult, type InferRemoveResultFromPath, type InferResultFromPath, type InferUpdateData, type InferUpdateDataFromPath, type InferUpdateResult, type InferUpdateResultFromPath, type InitDebounceMixinOptions, type MergeQueryOptions, type OnDeleteAction, type OnDeleteOptions, type OptimizeBatchPatchOptions, type OptimizeBatchPatchResultItem, type Predicate, type PredicateWithContext, type PushSetOptions, type RemoveRelatedOptions, type SetQueryKeySafelyOptions, type ShouldSkipOptions, checkMulti, createRelated, debounceMixin, defineHooks, filterArray, filterObject, filterQuery, forEach, getItemsIsArray, getPaginate, isMulti, isPaginated, makeDefaultOptions, markHookForSkip, mergeArrays, mergeQuery, onDelete, optimizeBatchPatch, parseFields, pushSet, removeRelated, runPerItem, setData, setQueryKeySafely, setResultEmpty, shouldSkip, toJSON, validateQueryProperty };
|
package/dist/index.d.ts
CHANGED
|
@@ -36,11 +36,17 @@ interface GetItemsIsArrayResult<T = any> {
|
|
|
36
36
|
}
|
|
37
37
|
declare const getItemsIsArray: <T = any, H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>>(context: H, options?: GetItemsIsArrayOptions) => GetItemsIsArrayResult<T>;
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
type HookForEachOptions<T = any, H = HookContext, R = never> = {
|
|
40
40
|
wait?: "sequential" | "parallel" | false;
|
|
41
41
|
items?: GetItemsIsArrayOptions["from"];
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
forAll?: (items: T[], context: H) => Promisable<R>;
|
|
43
|
+
};
|
|
44
|
+
type ActionPerItem<T, H, R> = (item: T, options: {
|
|
45
|
+
context: H;
|
|
46
|
+
i: number;
|
|
47
|
+
fromAll: R;
|
|
48
|
+
}) => Promisable<any>;
|
|
49
|
+
declare const forEach: <H extends HookContext<_feathersjs_feathers.Application<any, any>, any> = HookContext<_feathersjs_feathers.Application<any, any>, any>, T = any, R = never>(actionPerItem: ActionPerItem<T, H, R>, options?: HookForEachOptions<T, H, R>) => ReturnAsyncHook<H>;
|
|
44
50
|
|
|
45
51
|
type OnDeleteAction = "cascade" | "set null";
|
|
46
52
|
interface OnDeleteOptions {
|
|
@@ -234,6 +240,12 @@ declare const toJSON: (context: HookContext) => HookContext<_feathersjs_feathers
|
|
|
234
240
|
*/
|
|
235
241
|
declare const validateQueryProperty: (query: any, operators?: string[]) => Query;
|
|
236
242
|
|
|
243
|
+
type OptimizeBatchPatchOptions = {
|
|
244
|
+
id?: string;
|
|
245
|
+
};
|
|
246
|
+
type OptimizeBatchPatchResultItem<T = Record<string, unknown>, P = Params> = [Id, T, P | undefined];
|
|
247
|
+
declare function optimizeBatchPatch<T extends Record<string, unknown>, P extends Params>(items: Map<Id, T>, options?: OptimizeBatchPatchOptions): OptimizeBatchPatchResultItem<T, P>[];
|
|
248
|
+
|
|
237
249
|
declare const filterArray: <T extends string[]>(...keys: T) => { [key in T[number]]: (value: any, options: FilterQueryOptions) => any; };
|
|
238
250
|
|
|
239
251
|
declare const filterObject: <T extends string[]>(...keys: T) => { [key in T[number]]: (value: any, options: FilterQueryOptions) => any; };
|
|
@@ -284,4 +296,4 @@ type InferRemoveResultFromPath<App extends Application, Path extends string, IdO
|
|
|
284
296
|
type InferDataFromPath<App extends Application, Path extends string, Method extends "create" | "update" | "patch"> = Method extends "create" ? InferCreateDataFromPath<App, Path> : Method extends "update" ? InferUpdateDataFromPath<App, Path> : Method extends "patch" ? InferPatchDataFromPath<App, Path> : never;
|
|
285
297
|
type InferResultFromPath<App extends Application, Path extends string, Method extends "get" | "find" | "create" | "update" | "patch" | "remove"> = Method extends "get" ? InferGetResultFromPath<App, Path> : Method extends "find" ? InferFindResultFromPath<App, Path> : Method extends "create" ? InferCreateResultFromPath<App, Path> : Method extends "update" ? InferUpdateResultFromPath<App, Path> : Method extends "patch" ? InferPatchResultFromPath<App, Path> : Method extends "remove" ? InferRemoveResultFromPath<App, Path> : never;
|
|
286
298
|
|
|
287
|
-
export { type ActionOnEmptyIntersect, type CreateRelatedOptions, type DebouncedFunctionApp, type DebouncedService, DebouncedStore, type DebouncedStoreOptions, type FirstLast, type GetItemsIsArrayFrom, type GetItemsIsArrayOptions, type GetItemsIsArrayResult, type GetService, type Handle, type HookForEachOptions, type HookRunPerItemOptions, type HookSetDataOptions, type InferCreateData, type InferCreateDataFromPath, type InferCreateDataSingle, type InferCreateDataSingleFromPath, type InferCreateResult, type InferCreateResultFromPath, type InferCreateResultSingle, type InferCreateResultSingleFromPath, type InferDataFromPath, type InferFindResult, type InferFindResultFromPath, type InferGetResult, type InferGetResultFromPath, type InferPatchData, type InferPatchDataFromPath, type InferPatchResult, type InferPatchResultFromPath, type InferRemoveResult, type InferRemoveResultFromPath, type InferResultFromPath, type InferUpdateData, type InferUpdateDataFromPath, type InferUpdateResult, type InferUpdateResultFromPath, type InitDebounceMixinOptions, type MergeQueryOptions, type OnDeleteAction, type OnDeleteOptions, type Predicate, type PredicateWithContext, type PushSetOptions, type RemoveRelatedOptions, type SetQueryKeySafelyOptions, type ShouldSkipOptions, checkMulti, createRelated, debounceMixin, defineHooks, filterArray, filterObject, filterQuery, forEach, getItemsIsArray, getPaginate, isMulti, isPaginated, makeDefaultOptions, markHookForSkip, mergeArrays, mergeQuery, onDelete, parseFields, pushSet, removeRelated, runPerItem, setData, setQueryKeySafely, setResultEmpty, shouldSkip, toJSON, validateQueryProperty };
|
|
299
|
+
export { type ActionOnEmptyIntersect, type CreateRelatedOptions, type DebouncedFunctionApp, type DebouncedService, DebouncedStore, type DebouncedStoreOptions, type FirstLast, type GetItemsIsArrayFrom, type GetItemsIsArrayOptions, type GetItemsIsArrayResult, type GetService, type Handle, type HookForEachOptions, type HookRunPerItemOptions, type HookSetDataOptions, type InferCreateData, type InferCreateDataFromPath, type InferCreateDataSingle, type InferCreateDataSingleFromPath, type InferCreateResult, type InferCreateResultFromPath, type InferCreateResultSingle, type InferCreateResultSingleFromPath, type InferDataFromPath, type InferFindResult, type InferFindResultFromPath, type InferGetResult, type InferGetResultFromPath, type InferPatchData, type InferPatchDataFromPath, type InferPatchResult, type InferPatchResultFromPath, type InferRemoveResult, type InferRemoveResultFromPath, type InferResultFromPath, type InferUpdateData, type InferUpdateDataFromPath, type InferUpdateResult, type InferUpdateResultFromPath, type InitDebounceMixinOptions, type MergeQueryOptions, type OnDeleteAction, type OnDeleteOptions, type OptimizeBatchPatchOptions, type OptimizeBatchPatchResultItem, type Predicate, type PredicateWithContext, type PushSetOptions, type RemoveRelatedOptions, type SetQueryKeySafelyOptions, type ShouldSkipOptions, checkMulti, createRelated, debounceMixin, defineHooks, filterArray, filterObject, filterQuery, forEach, getItemsIsArray, getPaginate, isMulti, isPaginated, makeDefaultOptions, markHookForSkip, mergeArrays, mergeQuery, onDelete, optimizeBatchPatch, parseFields, pushSet, removeRelated, runPerItem, setData, setQueryKeySafely, setResultEmpty, shouldSkip, toJSON, validateQueryProperty };
|
package/dist/index.mjs
CHANGED
|
@@ -593,6 +593,30 @@ const validateQueryProperty = (query, operators = []) => {
|
|
|
593
593
|
};
|
|
594
594
|
};
|
|
595
595
|
|
|
596
|
+
function optimizeBatchPatch(items, options) {
|
|
597
|
+
const map = [];
|
|
598
|
+
const id = options?.id ?? "id";
|
|
599
|
+
for (const [id2, data] of items) {
|
|
600
|
+
const index = map.findIndex((item) => deepEqual(item.data, data));
|
|
601
|
+
if (index === -1) {
|
|
602
|
+
map.push({ ids: [id2], data });
|
|
603
|
+
} else {
|
|
604
|
+
map[index].ids.push(id2);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return map.map(({ ids, data }) => {
|
|
608
|
+
return ids.length === 1 ? [ids[0], data, void 0] : [
|
|
609
|
+
null,
|
|
610
|
+
data,
|
|
611
|
+
{
|
|
612
|
+
query: {
|
|
613
|
+
[id]: { $in: ids }
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
];
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
|
|
596
620
|
function checkMulti() {
|
|
597
621
|
return (context) => {
|
|
598
622
|
if (shouldSkip("checkMulti", context)) {
|
|
@@ -644,27 +668,30 @@ function createRelated({
|
|
|
644
668
|
};
|
|
645
669
|
}
|
|
646
670
|
|
|
647
|
-
const forEach = (actionPerItem,
|
|
648
|
-
const
|
|
649
|
-
wait: "parallel",
|
|
650
|
-
items: "automatic",
|
|
651
|
-
..._options
|
|
652
|
-
};
|
|
671
|
+
const forEach = (actionPerItem, options) => {
|
|
672
|
+
const { wait = "parallel", items: from = "automatic" } = options || {};
|
|
653
673
|
return async (context) => {
|
|
654
|
-
if (shouldSkip("
|
|
674
|
+
if (shouldSkip("forEach", context)) {
|
|
655
675
|
return context;
|
|
656
676
|
}
|
|
657
|
-
const { items } = getItemsIsArray(context, { from
|
|
677
|
+
const { items } = getItemsIsArray(context, { from });
|
|
678
|
+
const forAll = options?.forAll ? await options.forAll(items, context) : {};
|
|
658
679
|
const promises = [];
|
|
680
|
+
let i = 0;
|
|
659
681
|
for (const item of items) {
|
|
660
|
-
const promise = actionPerItem(item,
|
|
661
|
-
|
|
682
|
+
const promise = actionPerItem(item, {
|
|
683
|
+
context,
|
|
684
|
+
i,
|
|
685
|
+
fromAll: forAll
|
|
686
|
+
});
|
|
687
|
+
if (wait === "sequential") {
|
|
662
688
|
await promise;
|
|
663
689
|
} else {
|
|
664
690
|
promises.push(promise);
|
|
665
691
|
}
|
|
692
|
+
i++;
|
|
666
693
|
}
|
|
667
|
-
if (
|
|
694
|
+
if (wait === "parallel") {
|
|
668
695
|
await Promise.all(promises);
|
|
669
696
|
}
|
|
670
697
|
return context;
|
|
@@ -954,4 +981,4 @@ const filterObject = (...keys) => {
|
|
|
954
981
|
return result;
|
|
955
982
|
};
|
|
956
983
|
|
|
957
|
-
export { DebouncedStore, checkMulti, createRelated, debounceMixin, defineHooks, filterArray, filterObject, filterQuery, forEach, getItemsIsArray, getPaginate, isMulti, isPaginated, makeDefaultOptions, markHookForSkip, mergeArrays, mergeQuery, onDelete, parseFields, pushSet, removeRelated, runPerItem, setData, setQueryKeySafely, setResultEmpty, shouldSkip, toJSON, validateQueryProperty };
|
|
984
|
+
export { DebouncedStore, checkMulti, createRelated, debounceMixin, defineHooks, filterArray, filterObject, filterQuery, forEach, getItemsIsArray, getPaginate, isMulti, isPaginated, makeDefaultOptions, markHookForSkip, mergeArrays, mergeQuery, onDelete, optimizeBatchPatch, parseFields, pushSet, removeRelated, runPerItem, setData, setQueryKeySafely, setResultEmpty, shouldSkip, toJSON, validateQueryProperty };
|
package/package.json
CHANGED
package/src/hooks/forEach.ts
CHANGED
|
@@ -5,42 +5,63 @@ import type { HookContext } from "@feathersjs/feathers";
|
|
|
5
5
|
import type { GetItemsIsArrayOptions } from "../utils/getItemsIsArray";
|
|
6
6
|
import { getItemsIsArray } from "../utils/getItemsIsArray";
|
|
7
7
|
|
|
8
|
-
export
|
|
8
|
+
export type HookForEachOptions<T = any, H = HookContext, R = never> = {
|
|
9
9
|
wait?: "sequential" | "parallel" | false;
|
|
10
10
|
items?: GetItemsIsArrayOptions["from"];
|
|
11
|
-
|
|
11
|
+
forAll?: (items: T[], context: H) => Promisable<R>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type ActionPerItem<T, H, R> = (
|
|
15
|
+
item: T,
|
|
16
|
+
options: {
|
|
17
|
+
context: H;
|
|
18
|
+
i: number;
|
|
19
|
+
fromAll: R;
|
|
20
|
+
},
|
|
21
|
+
) => Promisable<any>;
|
|
12
22
|
|
|
13
|
-
export const forEach = <
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
export const forEach = <
|
|
24
|
+
H extends HookContext = HookContext,
|
|
25
|
+
T = any,
|
|
26
|
+
R = never,
|
|
27
|
+
>(
|
|
28
|
+
actionPerItem: ActionPerItem<T, H, R>,
|
|
29
|
+
options?: HookForEachOptions<T, H, R>,
|
|
17
30
|
): ReturnAsyncHook<H> => {
|
|
18
|
-
const
|
|
19
|
-
wait: "parallel",
|
|
20
|
-
items: "automatic",
|
|
21
|
-
..._options,
|
|
22
|
-
};
|
|
31
|
+
const { wait = "parallel", items: from = "automatic" } = options || {};
|
|
23
32
|
|
|
24
33
|
return async (context: H): Promise<H> => {
|
|
25
|
-
if (shouldSkip("
|
|
34
|
+
if (shouldSkip("forEach", context)) {
|
|
26
35
|
return context;
|
|
27
36
|
}
|
|
28
37
|
|
|
29
|
-
const { items } = getItemsIsArray(context, { from
|
|
38
|
+
const { items } = getItemsIsArray(context, { from });
|
|
39
|
+
|
|
40
|
+
const forAll = options?.forAll
|
|
41
|
+
? await options.forAll(items, context)
|
|
42
|
+
: ({} as R);
|
|
30
43
|
|
|
31
44
|
const promises: Promise<any>[] = [];
|
|
32
45
|
|
|
46
|
+
let i = 0;
|
|
47
|
+
|
|
33
48
|
for (const item of items) {
|
|
34
|
-
const promise = actionPerItem(item,
|
|
49
|
+
const promise = actionPerItem(item, {
|
|
50
|
+
context,
|
|
51
|
+
i,
|
|
52
|
+
fromAll: forAll,
|
|
53
|
+
});
|
|
35
54
|
|
|
36
|
-
if (
|
|
55
|
+
if (wait === "sequential") {
|
|
37
56
|
await promise;
|
|
38
57
|
} else {
|
|
39
58
|
promises.push(promise);
|
|
40
59
|
}
|
|
60
|
+
|
|
61
|
+
i++;
|
|
41
62
|
}
|
|
42
63
|
|
|
43
|
-
if (
|
|
64
|
+
if (wait === "parallel") {
|
|
44
65
|
await Promise.all(promises);
|
|
45
66
|
}
|
|
46
67
|
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Id, Params } from "@feathersjs/feathers";
|
|
2
|
+
import { deepEqual } from "fast-equals";
|
|
3
|
+
|
|
4
|
+
export type OptimizeBatchPatchOptions = {
|
|
5
|
+
id?: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type OptimizeBatchPatchResultItem<
|
|
9
|
+
T = Record<string, unknown>,
|
|
10
|
+
P = Params,
|
|
11
|
+
> = [Id, T, P | undefined];
|
|
12
|
+
|
|
13
|
+
export function optimizeBatchPatch<
|
|
14
|
+
T extends Record<string, unknown>,
|
|
15
|
+
P extends Params,
|
|
16
|
+
>(
|
|
17
|
+
items: Map<Id, T>,
|
|
18
|
+
options?: OptimizeBatchPatchOptions,
|
|
19
|
+
): OptimizeBatchPatchResultItem<T, P>[] {
|
|
20
|
+
const map: { ids: Id[]; data: T }[] = [];
|
|
21
|
+
|
|
22
|
+
const id = options?.id ?? "id";
|
|
23
|
+
|
|
24
|
+
for (const [id, data] of items) {
|
|
25
|
+
const index = map.findIndex((item) => deepEqual(item.data, data));
|
|
26
|
+
|
|
27
|
+
if (index === -1) {
|
|
28
|
+
map.push({ ids: [id], data });
|
|
29
|
+
} else {
|
|
30
|
+
map[index].ids.push(id);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return map.map(({ ids, data }) => {
|
|
35
|
+
return ids.length === 1
|
|
36
|
+
? ([ids[0], data, undefined] as OptimizeBatchPatchResultItem<T, P>)
|
|
37
|
+
: ([
|
|
38
|
+
null,
|
|
39
|
+
data,
|
|
40
|
+
{
|
|
41
|
+
query: {
|
|
42
|
+
[id]: { $in: ids },
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
] as OptimizeBatchPatchResultItem<T, P>);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (import.meta.vitest) {
|
|
50
|
+
const { it, expect } = import.meta.vitest;
|
|
51
|
+
it("optimizeBatchPatch", () => {
|
|
52
|
+
const items = new Map<Id, Record<string, unknown>>([
|
|
53
|
+
["1", { name: "John" }],
|
|
54
|
+
["2", { name: "Jane" }],
|
|
55
|
+
["3", { name: "John" }],
|
|
56
|
+
["4", { name: "Jane" }],
|
|
57
|
+
[5, { name: "Jack" }],
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
expect(optimizeBatchPatch(items)).toEqual([
|
|
61
|
+
[null, { name: "John" }, { query: { id: { $in: ["1", "3"] } } }],
|
|
62
|
+
[null, { name: "Jane" }, { query: { id: { $in: ["2", "4"] } } }],
|
|
63
|
+
[5, { name: "Jack" }, undefined],
|
|
64
|
+
]);
|
|
65
|
+
});
|
|
66
|
+
}
|