@prefecthq/prefect-ui-library
Version:
This library is the Vue and Typescript component library for [Prefect 2](https://github.com/PrefectHQ/prefect) and [Prefect Cloud 2](https://www.prefect.io/cloud/). _The components and utilities in this project are not meant to be used independently_.
178 lines (129 loc) • 5.64 kB
text/typescript
import { isDate } from 'date-fns'
import { unique } from '@/utilities/arrays'
export function flip<K extends string, V extends string>(obj: Record<K, V>): Record<V, K> {
const result = {} as Record<V, K>
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
result[obj[key]] = key
}
}
return result
}
export function omit<T extends Record<string, unknown>, K extends(keyof T)[]>(source: T, keys: K): Omit<T, K[number]> {
const copy = { ...source }
keys.forEach(key => delete copy[key])
return copy
}
export function clone<T>(source: T): T {
if (source === null || typeof source !== 'object') {
return source
}
if (isDate(source)) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
return new Date(source)
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
const copy = new source()
for (const key in source) {
copy[key] = clone(source[key])
}
return copy
}
export function hasProperty<T extends Record<string | symbol, unknown>>(needle: T, property: unknown): property is keyof T {
return (typeof property === 'string' || typeof property === 'symbol') && property in needle
}
export type MapKeysCallback<PreviousKey extends PropertyKey, Value, NewKey extends PropertyKey> = (key: PreviousKey, value: Value) => NewKey
export function mapKeys<K extends PropertyKey, V, Key extends PropertyKey>(object: Record<K, V>, callback: MapKeysCallback<K, V, Key>): Record<Key, V> {
const entries = Object.entries(object) as [K, V][]
const result = {} as Record<Key, V>
return entries.reduce((result, [key, value]) => {
const newKey = callback(key, value)
result[newKey] = object[key]
return result
}, result)
}
export type MapValuesCallback<Key extends PropertyKey, PreviousValue, NewValue> = (key: Key, value: PreviousValue) => NewValue
export function mapValues<Key extends PropertyKey, PreviousValue, NewValue>(object: Record<Key, PreviousValue>, callback: MapValuesCallback<Key, PreviousValue, NewValue>): Record<Key, NewValue> {
const entries = Object.entries(object) as [Key, PreviousValue][]
const result = {} as Record<Key, NewValue>
return entries.reduce((result, [key, value]) => {
result[key] = callback(key, value)
return result
}, result)
}
export type MapEntriesCallback<PreviousKey extends PropertyKey, PreviousValue, NewKey extends PropertyKey, NewValue> = (key: PreviousKey, value: PreviousValue) => [NewKey, NewValue]
export function mapEntries<PreviousKey extends PropertyKey, PreviousValue, NewKey extends PropertyKey, NewValue>(object: Partial<Record<PreviousKey, PreviousValue>>, callback: MapEntriesCallback<PreviousKey, PreviousValue, NewKey, NewValue>): Partial<Record<NewKey, NewValue>> {
const entries = Object.entries(object) as [PreviousKey, PreviousValue][]
const result = {} as Record<NewKey, NewValue>
return entries.reduce((result, [key, value]) => {
const [newKey, newValue] = callback(key, value)
result[newKey] = newValue
return result
}, result)
}
export function isEmptyObject(value: unknown): value is Record<string, never> {
return typeof value === 'object' && !Array.isArray(value) && value !== null && Object.keys(value).length === 0
}
export function isTypeRequired<T extends Record<PropertyKey, unknown>>(value: Partial<T>): value is Required<T> {
return Object.values(value).every(value => value !== undefined)
}
export function hasString<T extends Record<PropertyKey, unknown>>(obj: T, str: string): boolean {
const values = Object.values(obj).map(val => val?.toString().toLowerCase() ?? '').join('')
return values.includes(str.toLowerCase())
}
export function isRecord(item: unknown): item is Record<PropertyKey, unknown> {
return item !== null && typeof item === 'object' && !Array.isArray(item) && !isDate(item)
}
/**
* @deprecated Please use lodash.merge instead.
*/
export function merge<T extends Record<PropertyKey, unknown>>(target: T, ...sources: T[]): T {
if (sources.length === 0) {
return target
}
const [source, ...rest] = sources
const keys = unique([...Object.keys(target), ...Object.keys(source)])
for (const key of keys) {
const targetValue: unknown = target[key]
const sourceValue: unknown = source[key]
if (targetValue === sourceValue) {
continue
}
if (isRecord(targetValue) && isRecord(sourceValue)) {
merge(targetValue, sourceValue)
continue
}
if (isRecord(targetValue) && isRecord(source) && !(key in source)) {
merge(targetValue, { [key]: {} })
continue
}
// this is really sloppy typescript...
// eslint-disable-next-line @typescript-eslint/no-explicit-any
target[key as keyof T] = source[key] as any
}
return merge(target, ...rest)
}
type EmptyObjectsRemoved<T extends Record<PropertyKey, unknown>> = {
[P in keyof T]: T[P] extends Record<PropertyKey, unknown> ? EmptyObjectsRemoved<T[P]> | undefined : T[P];
}
export function removeEmptyObjects<T extends Record<PropertyKey, unknown>>(input: T): EmptyObjectsRemoved<T> {
const response: Record<PropertyKey, unknown> = {}
const keys = Object.keys(input)
for (const key of keys) {
const value = input[key]
if (value === undefined) {
continue
}
if (isRecord(value)) {
const possiblyEmptyObject = removeEmptyObjects(value)
if (Object.keys(possiblyEmptyObject).length) {
response[key] = possiblyEmptyObject
}
continue
}
response[key] = value
}
return response as EmptyObjectsRemoved<T>
}