Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@stackbit/utils

Package Overview
Dependencies
Maintainers
13
Versions
311
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@stackbit/utils - npm Package Compare versions

Comparing version 0.2.10 to 0.2.11

__tests__/test-utils/toBeWithinRange.d.ts

314

__tests__/promise-utils.ts
import { jest, describe, expect, test } from '@jest/globals';
import './test-utils/toBeWithinRange';
import { deferredPromise, deferWhileRunning } from '../src';

@@ -7,2 +8,8 @@

function sleep(ms: number) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
describe('deferredPromise', () => {

@@ -97,2 +104,141 @@ test('resolves when resolve is called', async () => {

test('debounce the function call and resolves with the resolved value after debounce delay', async () => {
const callTimes: number[] = [];
const mockFn = jest.fn(async (arg1: string, arg2: string, arg3: string) => {
callTimes.push(Date.now());
return arg1 + arg2 + arg3;
});
const deferred = deferWhileRunning(mockFn, { debounceDelay: 100 });
const start = Date.now();
const result = deferred('a', 'b', 'c');
await expect(result).resolves.toEqual('abc');
expect(callTimes[0]! - start).toBeWithinRange(80, 120);
expect(mockFn.mock.calls.length).toEqual(1);
expect(mockFn.mock.lastCall).toEqual(['a', 'b', 'c']);
});
test('debounce the function call and rejects with the rejected value after debounce delay', async () => {
const callTimes: number[] = [];
const mockFn = jest.fn(async (arg1: string, arg2: string, arg3: string) => {
callTimes.push(Date.now());
return Promise.reject({ error: arg1 + arg2 + arg3 });
});
const deferred = deferWhileRunning(mockFn, { debounceDelay: 100 });
const start = Date.now();
const result = deferred('a', 'b', 'c');
await expect(result).rejects.toEqual({ error: 'abc' });
expect(callTimes[0]! - start).toBeWithinRange(80, 120);
expect(mockFn.mock.calls.length).toEqual(1);
expect(mockFn.mock.lastCall).toEqual(['a', 'b', 'c']);
});
test('debounce the function call and rejects with the thrown error after debounce delay', async () => {
const callTimes: number[] = [];
const mockFn = jest.fn(async (arg1: string, arg2: string, arg3: string) => {
callTimes.push(Date.now());
throw new Error(arg1 + arg2 + arg3);
});
const deferred = deferWhileRunning(mockFn, { debounceDelay: 100 });
const start = Date.now();
const result = deferred('a', 'b', 'c');
await expect(result).rejects.toThrow('abc');
expect(callTimes[0]! - start).toBeWithinRange(80, 120);
expect(mockFn.mock.calls.length).toEqual(1);
expect(mockFn.mock.lastCall).toEqual(['a', 'b', 'c']);
});
test('debounce the function three times then invokes the function with the last provided arguments and resolves all calls after debounce delay', async () => {
const callTimes: number[] = [];
const mockFn = jest.fn(async (arg1: string) => {
callTimes.push(Date.now());
return arg1;
});
const deferred = deferWhileRunning(mockFn, { debounceDelay: 150 });
const start = Date.now();
// first call is debounced for 150ms
const result1 = deferred('a');
await sleep(100);
// second call is debounced for 150ms after 100ms
const result2 = deferred('b');
await sleep(100);
// third call is debounced for 150ms after 200ms and called at 350ms
const result3 = deferred('c');
await expect(result1).resolves.toEqual('c');
await expect(result2).resolves.toEqual('c');
await expect(result3).resolves.toEqual('c');
expect(callTimes[0]! - start).toBeWithinRange(330, 370);
expect(mockFn.mock.calls.length).toEqual(1);
expect(mockFn.mock.calls[0]).toEqual(['c']);
});
test('debounce the function twice then invokes the function due to debounceMaxDelay, then defers the third call until the previous invocation is resolved', async () => {
const callTimes: number[] = [];
const mockFn = jest.fn(async (arg1: string) => {
callTimes.push(Date.now());
await sleep(100);
return arg1;
});
const deferred = deferWhileRunning(mockFn, {
debounceDelay: 300,
debounceMaxDelay: 400
});
const start = Date.now();
// first call is debounced for 300ms
const result1 = deferred('a');
await sleep(250);
// second call is debounced for 150ms because max debounce delay is 400ms
const result2 = deferred('b');
// the second call will be invoked after 150ms and run for 100ms
await sleep(200);
// after 200ms the second call will be still running, and the third call will be deferred
const result3 = deferred('c');
await expect(result1).resolves.toEqual('b');
await expect(result2).resolves.toEqual('b');
await expect(result3).resolves.toEqual('c');
expect(mockFn.mock.calls.length).toEqual(2);
expect(callTimes[0]! - start).toBeWithinRange(380, 420);
expect(callTimes[1]! - callTimes[0]!).toBeWithinRange(80, 120);
expect(mockFn.mock.calls[0]).toEqual(['b']);
expect(mockFn.mock.calls[1]).toEqual(['c']);
});
test('debounce the function three times then invokes the function with arguments as defined by the argsResolver then resolves with the resolved value after debounce delay', async () => {
const callTimes: number[] = [];
const mockFn = jest.fn(async (arg1: string, arg2: number) => {
callTimes.push(Date.now());
return [arg1, arg2];
});
const deferred = deferWhileRunning(mockFn, {
debounceDelay: 100,
argsResolver: ({ nextArgs, prevArgs }) => {
return prevArgs ? [prevArgs[0] + nextArgs[0], prevArgs[1] + nextArgs[1]] as [string, number] : nextArgs;
}
});
const start = Date.now();
// first call is debounced for 100ms
const result1 = deferred('a', 1);
await sleep(50);
// second call is debounced for 100ms after 50ms
const result2 = deferred('b', 2);
await sleep(50);
// third call is debounced for 100ms after 100ms and called after 200ms
const result3 = deferred('c', 3);
await expect(result1).resolves.toEqual(['abc', 6]);
await expect(result2).resolves.toEqual(['abc', 6]);
await expect(result3).resolves.toEqual(['abc', 6]);
expect(callTimes[0]! - start).toBeWithinRange(180, 220);
expect(mockFn.mock.calls.length).toEqual(1);
expect(mockFn.mock.calls[0]).toEqual(['abc', 6]);
});
test('defers the next function call until the previous invocation is resolved', async () => {

@@ -212,3 +358,3 @@ const wait = deferredPromise<void>();

'defers multiple function calls until the previous invocation is resolved,' +
'then invokes the function with the last provided arguments and rejects all calls',
'then invokes the function with the last provided arguments and rejects all calls',
async () => {

@@ -223,3 +369,3 @@ const wait = deferredPromise<void>();

.mockImplementationOnce(async (arg1: string, arg2: string, arg3: string) => {
return Promise.reject({ error: arg1 + arg2 + arg3 })
return Promise.reject({ error: arg1 + arg2 + arg3 });
});

@@ -303,92 +449,98 @@ const deferred = deferWhileRunning(mockFn);

test('defers multiple function calls in a group until the previous invocation in the same group is resolved,' +
'then invokes the function with the last provided arguments to the group and resolves all group calls', async () => {
const wait = deferredPromise<void>();
const mockFn = jest.fn(async (arg1: string, arg2: string, arg3: string) => {
await wait.promise;
return arg1 + '-' + arg2 + arg3;
});
const deferred = deferWhileRunning(mockFn, {
groupResolver: (group: string) => group
});
const result1a = deferred('group1', 'a', 'b');
const result1b = deferred('group1', 'c', 'd');
const result1c = deferred('group1', 'e', 'f');
const result2a = deferred('group2', 'a', 'b');
const result2b = deferred('group2', 'c', 'd');
const result2c = deferred('group2', 'e', 'f');
const result3a = deferred('group3', 'a', 'b');
const result3b = deferred('group3', 'c', 'd');
const result3c = deferred('group3', 'e', 'f');
test(
'defers multiple function calls in a group until the previous invocation in the same group is resolved,' +
'then invokes the function with the last provided arguments to the group and resolves all group calls',
async () => {
const wait = deferredPromise<void>();
const mockFn = jest.fn(async (arg1: string, arg2: string, arg3: string) => {
await wait.promise;
return arg1 + '-' + arg2 + arg3;
});
const deferred = deferWhileRunning(mockFn, {
groupResolver: (group: string) => group
});
const result1a = deferred('group1', 'a', 'b');
const result1b = deferred('group1', 'c', 'd');
const result1c = deferred('group1', 'e', 'f');
const result2a = deferred('group2', 'a', 'b');
const result2b = deferred('group2', 'c', 'd');
const result2c = deferred('group2', 'e', 'f');
const result3a = deferred('group3', 'a', 'b');
const result3b = deferred('group3', 'c', 'd');
const result3c = deferred('group3', 'e', 'f');
expect(mockFn.mock.calls.length).toEqual(3);
expect(mockFn.mock.calls[0]).toEqual(['group1', 'a', 'b']);
expect(mockFn.mock.calls[1]).toEqual(['group2', 'a', 'b']);
expect(mockFn.mock.calls[2]).toEqual(['group3', 'a', 'b']);
expect(mockFn.mock.calls.length).toEqual(3);
expect(mockFn.mock.calls[0]).toEqual(['group1', 'a', 'b']);
expect(mockFn.mock.calls[1]).toEqual(['group2', 'a', 'b']);
expect(mockFn.mock.calls[2]).toEqual(['group3', 'a', 'b']);
wait.resolve();
wait.resolve();
await expect(result1a).resolves.toEqual('group1-ab');
await expect(result2a).resolves.toEqual('group2-ab');
await expect(result3a).resolves.toEqual('group3-ab');
await expect(result1a).resolves.toEqual('group1-ab');
await expect(result2a).resolves.toEqual('group2-ab');
await expect(result3a).resolves.toEqual('group3-ab');
await expect(result1b).resolves.toEqual('group1-ef');
await expect(result1c).resolves.toEqual('group1-ef');
await expect(result2b).resolves.toEqual('group2-ef');
await expect(result2c).resolves.toEqual('group2-ef');
await expect(result3b).resolves.toEqual('group3-ef');
await expect(result3c).resolves.toEqual('group3-ef');
await expect(result1b).resolves.toEqual('group1-ef');
await expect(result1c).resolves.toEqual('group1-ef');
await expect(result2b).resolves.toEqual('group2-ef');
await expect(result2c).resolves.toEqual('group2-ef');
await expect(result3b).resolves.toEqual('group3-ef');
await expect(result3c).resolves.toEqual('group3-ef');
expect(mockFn.mock.calls.length).toEqual(6);
expect(mockFn.mock.calls[3]).toEqual(['group1', 'e', 'f']);
expect(mockFn.mock.calls[4]).toEqual(['group2', 'e', 'f']);
expect(mockFn.mock.calls[5]).toEqual(['group3', 'e', 'f']);
});
expect(mockFn.mock.calls.length).toEqual(6);
expect(mockFn.mock.calls[3]).toEqual(['group1', 'e', 'f']);
expect(mockFn.mock.calls[4]).toEqual(['group2', 'e', 'f']);
expect(mockFn.mock.calls[5]).toEqual(['group3', 'e', 'f']);
}
);
test('defers multiple function calls in a group until the previous invocation in the same group is resolved,' +
'then invokes the function with arguments as defined by argsResolver to the group and resolves all group calls', async () => {
const wait = deferredPromise<void>();
const mockFn = jest.fn<MockFunction>(async (arg1: string, arg2: string, arg3: string) => {
await wait.promise;
return arg1 + '-' + arg2 + arg3;
});
const deferred = deferWhileRunning(mockFn, {
groupResolver: (group: string) => group,
argsResolver: ({nextArgs, prevArgs}) => {
return [nextArgs[0], 'A', 'B'] as [string, string, string];
}
});
const result1a = deferred('group1', 'a', 'b');
const result1b = deferred('group1', 'c', 'd');
const result1c = deferred('group1', 'e', 'f');
const result2a = deferred('group2', 'a', 'b');
const result2b = deferred('group2', 'c', 'd');
const result2c = deferred('group2', 'e', 'f');
const result3a = deferred('group3', 'a', 'b');
const result3b = deferred('group3', 'c', 'd');
const result3c = deferred('group3', 'e', 'f');
test(
'defers multiple function calls in a group until the previous invocation in the same group is resolved,' +
'then invokes the function with arguments as defined by argsResolver to the group and resolves all group calls',
async () => {
const wait = deferredPromise<void>();
const mockFn = jest.fn<MockFunction>(async (arg1: string, arg2: string, arg3: string) => {
await wait.promise;
return arg1 + '-' + arg2 + arg3;
});
const deferred = deferWhileRunning(mockFn, {
groupResolver: (group: string) => group,
argsResolver: ({ nextArgs, prevArgs }) => {
return [nextArgs[0], 'A', 'B'] as [string, string, string];
}
});
const result1a = deferred('group1', 'a', 'b');
const result1b = deferred('group1', 'c', 'd');
const result1c = deferred('group1', 'e', 'f');
const result2a = deferred('group2', 'a', 'b');
const result2b = deferred('group2', 'c', 'd');
const result2c = deferred('group2', 'e', 'f');
const result3a = deferred('group3', 'a', 'b');
const result3b = deferred('group3', 'c', 'd');
const result3c = deferred('group3', 'e', 'f');
expect(mockFn.mock.calls.length).toEqual(3);
expect(mockFn.mock.calls[0]).toEqual(['group1', 'a', 'b']);
expect(mockFn.mock.calls[1]).toEqual(['group2', 'a', 'b']);
expect(mockFn.mock.calls[2]).toEqual(['group3', 'a', 'b']);
expect(mockFn.mock.calls.length).toEqual(3);
expect(mockFn.mock.calls[0]).toEqual(['group1', 'a', 'b']);
expect(mockFn.mock.calls[1]).toEqual(['group2', 'a', 'b']);
expect(mockFn.mock.calls[2]).toEqual(['group3', 'a', 'b']);
wait.resolve();
wait.resolve();
await expect(result1a).resolves.toEqual('group1-ab');
await expect(result2a).resolves.toEqual('group2-ab');
await expect(result3a).resolves.toEqual('group3-ab');
await expect(result1a).resolves.toEqual('group1-ab');
await expect(result2a).resolves.toEqual('group2-ab');
await expect(result3a).resolves.toEqual('group3-ab');
await expect(result1b).resolves.toEqual('group1-AB');
await expect(result1c).resolves.toEqual('group1-AB');
await expect(result2b).resolves.toEqual('group2-AB');
await expect(result2c).resolves.toEqual('group2-AB');
await expect(result3b).resolves.toEqual('group3-AB');
await expect(result3c).resolves.toEqual('group3-AB');
await expect(result1b).resolves.toEqual('group1-AB');
await expect(result1c).resolves.toEqual('group1-AB');
await expect(result2b).resolves.toEqual('group2-AB');
await expect(result2c).resolves.toEqual('group2-AB');
await expect(result3b).resolves.toEqual('group3-AB');
await expect(result3c).resolves.toEqual('group3-AB');
expect(mockFn.mock.calls.length).toEqual(6);
expect(mockFn.mock.calls[3]).toEqual(['group1', 'A', 'B']);
expect(mockFn.mock.calls[4]).toEqual(['group2', 'A', 'B']);
expect(mockFn.mock.calls[5]).toEqual(['group3', 'A', 'B']);
});
expect(mockFn.mock.calls.length).toEqual(6);
expect(mockFn.mock.calls[3]).toEqual(['group1', 'A', 'B']);
expect(mockFn.mock.calls[4]).toEqual(['group2', 'A', 'B']);
expect(mockFn.mock.calls[5]).toEqual(['group3', 'A', 'B']);
}
);
});

@@ -46,3 +46,5 @@ export declare function forEachPromise<T>(array: T[], callback: (value: T, index: number, array: T[]) => Promise<any>, thisArg?: any): Promise<void>;

}) => Parameters<T>;
debounceDelay?: number;
debounceMaxDelay?: number;
}): T;
//# sourceMappingURL=promise-utils.d.ts.map

@@ -156,2 +156,5 @@ "use strict";

const argsResolver = (_b = options.argsResolver) !== null && _b !== void 0 ? _b : (({ nextArgs }) => nextArgs);
const thisArg = options.thisArg;
const debounceDelay = options.debounceDelay;
const debounceMaxDelay = options.debounceMaxDelay;
const deferGroups = {};

@@ -161,3 +164,3 @@ const invoke = async (group, args) => {

group.isInvoking = true;
return await func.apply(options.thisArg, args);
return await func.apply(thisArg, args);
}

@@ -183,2 +186,31 @@ catch (e) {

};
const debounce = (group, args, debounceWait) => {
const now = new Date().getTime();
if (!group.deferred) {
group.startWaiting = now;
group.deferred = deferredPromise();
}
if (group.timeout) {
clearTimeout(group.timeout);
}
group.nextArgs = argsResolver({ nextArgs: args, prevArgs: group.nextArgs });
const wait = debounceMaxDelay
? Math.min(Math.max(0, debounceMaxDelay - (now - group.startWaiting)), debounceWait)
: debounceWait;
group.timeout = setTimeout(() => {
const nextArgsCopy = group.nextArgs;
const deferredCopy = group.deferred;
group.nextArgs = null;
group.deferred = null;
group.timeout = null;
group.startWaiting = null;
try {
deferredCopy.resolve(invoke(group, nextArgsCopy));
}
catch (e) {
deferredCopy.reject(e);
}
}, wait);
return group.deferred.promise;
};
return (async (...args) => {

@@ -192,2 +224,4 @@ const groupId = groupResolver(...args);

group = {
startWaiting: null,
timeout: null,
isInvoking: false,

@@ -200,5 +234,8 @@ deferred: null,

if (!group.isInvoking) {
if (typeof debounceDelay !== 'undefined') {
return debounce(group, args, debounceDelay);
}
return invoke(group, args);
}
else if (!group.deferred) {
if (!group.deferred) {
group.deferred = deferredPromise();

@@ -205,0 +242,0 @@ }

module.exports = {
testPathIgnorePatterns: [],
testPathIgnorePatterns: ['__tests__/test-utils'],
preset: 'ts-jest',
testEnvironment: 'node'
};
{
"name": "@stackbit/utils",
"version": "0.2.10",
"version": "0.2.11",
"description": "Stackbit utilities",

@@ -46,3 +46,3 @@ "main": "dist/index.js",

},
"gitHead": "2510e480447f8f79424f80a3c7b782e2f42fe730"
"gitHead": "a63161f72fb4ca99c360c2639870d1a6cc130a33"
}

@@ -164,11 +164,18 @@ export function forEachPromise<T>(array: T[], callback: (value: T, index: number, array: T[]) => Promise<any>, thisArg?: any): Promise<void> {

options: {
thisArg?: any,
thisArg?: any;
groupResolver?: (...args: Parameters<T>) => string;
argsResolver?: ({ nextArgs, prevArgs }: { nextArgs: Parameters<T>; prevArgs: Parameters<T> | null }) => Parameters<T>;
} = {},
debounceDelay?: number;
debounceMaxDelay?: number;
} = {}
): T {
const groupResolver = options.groupResolver ?? (() => 'defaultGroup');
const argsResolver = options.argsResolver ?? (({ nextArgs }) => nextArgs);
const thisArg = options.thisArg;
const debounceDelay = options.debounceDelay;
const debounceMaxDelay = options.debounceMaxDelay;
type DeferGroup = {
startWaiting: number | null;
timeout: NodeJS.Timeout | null;
isInvoking: boolean;

@@ -183,3 +190,3 @@ deferred: DeferredPromise<R> | null;

group.isInvoking = true;
return await func.apply(options.thisArg, args);
return await func.apply(thisArg, args);
} catch (e) {

@@ -203,5 +210,35 @@ throw e;

const debounce = (group: DeferGroup, args: Parameters<T>, debounceWait: number): Promise<R> => {
const now = new Date().getTime();
if (!group.deferred) {
group.startWaiting = now;
group.deferred = deferredPromise();
}
if (group.timeout) {
clearTimeout(group.timeout);
}
group.nextArgs = argsResolver({ nextArgs: args, prevArgs: group.nextArgs });
const wait = debounceMaxDelay
? Math.min(Math.max(0, debounceMaxDelay - (now - group.startWaiting!)), debounceWait)
: debounceWait;
group.timeout = setTimeout(() => {
const nextArgsCopy = group.nextArgs!;
const deferredCopy = group.deferred!;
group.nextArgs = null;
group.deferred = null;
group.timeout = null;
group.startWaiting = null;
try {
deferredCopy.resolve(invoke(group, nextArgsCopy));
} catch (e) {
deferredCopy.reject(e);
}
}, wait);
return group.deferred.promise;
};
return (async (...args: Parameters<T>): Promise<R> => {
const groupId = groupResolver(...args);
let group: DeferGroup;
if (groupId in deferGroups) {

@@ -211,2 +248,4 @@ group = deferGroups[groupId]!;

group = {
startWaiting: null,
timeout: null,
isInvoking: false,

@@ -218,7 +257,14 @@ deferred: null,

}
if (!group.isInvoking) {
if (typeof debounceDelay !== 'undefined') {
return debounce(group, args, debounceDelay);
}
return invoke(group, args);
} else if (!group.deferred) {
}
if (!group.deferred) {
group.deferred = deferredPromise();
}
group.nextArgs = argsResolver({ nextArgs: args, prevArgs: group.nextArgs });

@@ -225,0 +271,0 @@ return group.deferred.promise;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc