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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 1 addition & 18 deletions packages/core/src/auth/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { SsoClient } from './sso/clients'
import { getLogger } from '../shared/logger'
import { CredentialsProviderManager } from './providers/credentialsProviderManager'
import { asString, CredentialsId, CredentialsProvider, fromString } from './providers/credentials'
import { once } from '../shared/utilities/functionUtils'
import { keyedDebounce, once } from '../shared/utilities/functionUtils'
import { CredentialsSettings } from './credentials/utils'
import {
extractDataFromSection,
Expand Down Expand Up @@ -104,23 +104,6 @@ interface AuthService {
updateConnection(connection: Pick<Connection, 'id'>, profile: Profile): Promise<Connection>
}

function keyedDebounce<T, U extends any[], K extends string = string>(
fn: (key: K, ...args: U) => Promise<T>
): typeof fn {
const pending = new Map<K, Promise<T>>()

return (key, ...args) => {
if (pending.has(key)) {
return pending.get(key)!
}

const promise = fn(key, ...args).finally(() => pending.delete(key))
pending.set(key, promise)

return promise
}
}

export interface ConnectionStateChangeEvent {
readonly id: Connection['id']
readonly state: ProfileMetadata['connectionState']
Expand Down
65 changes: 38 additions & 27 deletions packages/core/src/shared/utilities/functionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,38 +85,28 @@ export function memoize<T, U extends any[]>(fn: (...args: U) => T): (...args: U)
* Multiple calls made during the debounce window will receive references to the
* same Promise similar to {@link shared}. The window will also be 'rolled', delaying
* the execution by another {@link delay} milliseconds.
*
* This function prevents execution until {@link delay} milliseconds have passed
* since the last invocation regardless of arguments. If this should be
* argument dependent, look into {@link keyedDebounce}
*/
export function debounce<T>(cb: () => T | Promise<T>, delay: number = 0): () => Promise<T> {
let timeout: Timeout | undefined
let promise: Promise<T> | undefined

return () => {
timeout?.refresh()

return (promise ??= new Promise<T>((resolve, reject) => {
timeout = new Timeout(delay)
timeout.onCompletion(async () => {
timeout = promise = undefined
try {
resolve(await cb())
} catch (err) {
reject(err)
}
})
}))
}
export function debounce<Input extends any[], Output>(
cb: (...args: Input) => Output | Promise<Output>,
delay: number = 0
): (...args: Input) => Promise<Output> {
return cancellableDebounce(cb, delay).promise
}

/**
*
* Similar to {@link debounce}, but allows the function to be cancelled and allow callbacks to pass function parameters.
* Similar to {@link debounce}, but allows the function to be cancelled.
*/
export function cancellableDebounce<T, U extends any[]>(
cb: (...args: U) => T | Promise<T>,
export function cancellableDebounce<Input extends any[], Output>(
cb: (...args: Input) => Output | Promise<Output>,
delay: number = 0
): { promise: (...args: U) => Promise<T>; cancel: () => void } {
): { promise: (...args: Input) => Promise<Output>; cancel: () => void } {
let timeout: Timeout | undefined
let promise: Promise<T> | undefined
let promise: Promise<Output> | undefined

const cancel = (): void => {
if (timeout) {
Expand All @@ -127,15 +117,15 @@ export function cancellableDebounce<T, U extends any[]>(
}

return {
promise: (...arg) => {
promise: (...args: Input) => {
timeout?.refresh()

return (promise ??= new Promise<T>((resolve, reject) => {
return (promise ??= new Promise<Output>((resolve, reject) => {
timeout = new Timeout(delay)
timeout.onCompletion(async () => {
timeout = promise = undefined
try {
resolve(await cb(...arg))
resolve(await cb(...args))
} catch (err) {
reject(err)
}
Expand All @@ -145,3 +135,24 @@ export function cancellableDebounce<T, U extends any[]>(
cancel: cancel,
}
}

/**
*
* Similar to {@link debounce}, but uses a key to determine if the function should be called yet rather than a timeout connected to the function itself.
*/
export function keyedDebounce<T, U extends any[], K extends string = string>(
fn: (key: K, ...args: U) => Promise<T>
): typeof fn {
const pending = new Map<K, Promise<T>>()

return (key, ...args) => {
if (pending.has(key)) {
return pending.get(key)!
}

const promise = fn(key, ...args).finally(() => pending.delete(key))
pending.set(key, promise)

return promise
}
}
Loading