/* eslint-disable @typescript-eslint/no-unsafe-function-type */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-redundant-type-constituents */ import * as Cause from "effect/Cause" import * as Effect from "effect/Effect" import * as Exit from "effect/Exit" import * as Fiber from "effect/Fiber" import { dual } from "effect/Function" import * as Option from "effect/Option" import { isFunction } from "effect/Predicate" import * as Record from "effect/Record" import * as Result from "effect/Result" import { identity, pipe } from "./Function.js" import type { DeepMutable, Equals, Mutable } from "./Types.js" // codegen:start {preset: barrel, include: ./utils/*.ts, nodir: false } export * from "./utils/effectify.ts" export * from "./utils/extend.ts" export * from "./utils/gen.ts" export * from "./utils/logger.ts" export * from "./utils/logLevel.ts" // codegen:end export * from "effect/Utils" export const cloneTrait = Symbol.for("clone-trait") export interface Clone { [cloneTrait](this: this, that: any): this } export const unsafeRight = (ei: Result.Result) => { if (Result.isFailure(ei)) { console.error(ei.failure) throw ei.failure } return ei.success } export const unsafeSome = (makeErrorMessage: () => string) => (o: Option.Option) => { if (Option.isNone(o)) { throw new Error(makeErrorMessage()) } return o.value } export function toString(v: unknown) { return `${v}` } export const typedKeysOf = (obj: T) => Object.keys(obj) as (keyof T)[] // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion export const typedValuesOf = (obj: T) => Object.values(obj) as ValueOf[] type ValueOf = T[keyof T] export type Constructor = { new(...args: any[]): T } export type ThenArg = T extends Promise ? U : T extends (...args: any[]) => Promise ? V : T /** * @deprecated TODO: does not filter optional fields! */ type NoUndefinedField = { [P in keyof T]: Exclude } export function dropUndefinedT>( input: A ): NoUndefinedField { const newR = pipe( input, Record.filter((x): x is A => x !== undefined) ) return newR as any } type IsOptional, K extends keyof T> = {} extends Pick ? true : false type WithUndefined> = { [K in keyof T]: IsOptional extends true ? T[K] | undefined : T[K] } type WithUndefinedRecursive> = { [K in keyof T]: WithUndefinedRecursive extends infer $RTK ? IsOptional extends true ? $RTK | undefined : $RTK : never } /** a version of @see dropUndefinedT that keeps support for go to definition, auto completion, and documentation of fields! */ export function dropUndefinedT2>() { return ( input: WithUndefined ): Desired => { const newR = pipe( input as any, Record.filter((x): x is Desired => x !== undefined) ) return newR as any } } /** * A recursive version of @see dropUndefinedT that preserves IDE support for * go-to-definition, auto-completion, and field documentation. * * Note: This utility does not physically remove `undefined` fields at runtime. * It provides type-level compatibility with libraries that use `prop?: type` * syntax. Before the introduction of `exactOptionalPropertyTypes`, this syntax * implicitly allowed `undefined` values. Libraries that adopted `prop?: type` * before this flag was introduced still accept `undefined` at runtime. */ export function dropUndefinedRec>() { return ( input: WithUndefinedRecursive ): Desired => { // doesn't drop for real return input as any } } export type Dictionary = { readonly [P in string]: T } export function dropUndefined( input: Dictionary ): Dictionary { const newR = pipe( input, Record.filter((x): x is A => x !== undefined) ) return newR } type GetTag = T extends { _tag: infer K } ? K : never export const isOfType = (tag: GetTag) => (e: { _tag: string }): e is T => e._tag === tag export function capitalize(string: T): Capitalize { return (string.charAt(0).toUpperCase() + string.slice(1)) as Capitalize } export function uncapitalize(string: T): Uncapitalize { return (string.charAt(0).toLowerCase() + string.slice(1)) as Uncapitalize } export function pretty(o: unknown): string { return JSON.stringify(o, undefined, 2) ?? "undefined" } export type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( k: infer I ) => void ? I : never // generally A | B is not assignable to A & B export type IsUnion = [T] extends [UnionToIntersection] ? false : true export type EnforceNonEmptyRecord = keyof R extends never ? never : R export function intersect( ...as: AS ): UnionToIntersection<{ [k in keyof AS]: AS[k] }[number]> { return as.reduce((a: any, b: any) => Object.assign(a, b), {}) as any } export type IsEqualTo = (() => T extends X ? 1 : 2) extends < T >() => T extends Y ? 1 : 2 ? true : false export const unifyIndex = Symbol() export type unifyIndex = typeof unifyIndex export interface UnifiableIndexed<_X> {} export type UnifiableIndexedURI = keyof UnifiableIndexed export interface Unifiable { // Sync: [X] extends [Sync] ? Sync : never // Effect: [X] extends [Effect] // ? [X] extends [Sync] // ? never // : Effect.Effect // : never Unify: [X] extends [{ readonly [unifyIndex]: infer K }] ? K extends UnifiableIndexedURI ? UnifiableIndexed[K] : never : never } export type Unify = Unifiable[keyof Unifiable] extends never ? X : Unifiable[keyof Unifiable] // forked from https://github.com/Alorel/typescript-lazy-get-decorator type DecoratorReturn = PropertyDescriptor | NewDescriptor function decorateNew( inp: NewDescriptor, setProto: boolean, makeNonConfigurable: boolean, resultSelector: ResultSelectorFn ): NewDescriptor { const out: NewDescriptor = Object.assign({}, inp) if (out.descriptor) { out.descriptor = Object.assign({}, out.descriptor) } const actualDesc: PropertyDescriptor = out.descriptor || /* istanbul ignore next */ out const originalMethod = validateAndExtractMethodFromDescriptor(actualDesc) const isStatic = inp.placement === "static" actualDesc.get = function(this: any): any { return getterCommon( isStatic ? this : Object.getPrototypeOf(this), out.key, isStatic, !!actualDesc.enumerable, originalMethod, this, // eslint-disable-next-line prefer-rest-params arguments, setProto, makeNonConfigurable, resultSelector ) } return out } function decorateLegacy( target: any, key: PropertyKey, descriptor: PropertyDescriptor, setProto: boolean, makeNonConfigurable: boolean, // tslint:enable:bool-param-default resultSelector: ResultSelectorFn ): PropertyDescriptor { /* istanbul ignore if */ if (!descriptor) { descriptor = Object.getOwnPropertyDescriptor(target, key) if (!descriptor) { const e = new Error("@LazyGetter is unable to determine the property descriptor") ;( e).$target = target ;( e).$key = key throw e } } const originalMethod = validateAndExtractMethodFromDescriptor(descriptor) return Object.assign({}, descriptor, { get(this: any): any { return getterCommon( target, key, Object.getPrototypeOf(target) === Function.prototype, !!descriptor.enumerable, originalMethod, this, // eslint-disable-next-line prefer-rest-params arguments, setProto, makeNonConfigurable, resultSelector ) } }) } /** Signifies that the modified property descriptor can be reset to its original state */ interface ResettableDescriptor { /** * Restore the property descriptor on the given class instance or prototype and re-apply the lazy getter. * @param on The class instance or prototype */ reset(on: any): void } /** ES7 proposal descriptor, tweaked for Babel */ interface NewDescriptor extends PropertyDescriptor { descriptor?: PropertyDescriptor key: PropertyKey kind: string placement: string } /** A filter function that must return true for the value to cached */ export type ResultSelectorFn = (v: any) => boolean function defaultFilter(): boolean { return true } function validateAndExtractMethodFromDescriptor(desc: PropertyDescriptor): Function { // eslint-disable-next-line @typescript-eslint/unbound-method const originalMethod = desc.get if (!originalMethod) { throw new Error("@LazyGetter can only decorate getters!") } else if (!desc.configurable) { throw new Error("@LazyGetter target must be configurable") } return originalMethod } function getterCommon( // tslint:disable-line:parameters-max-number target: any, key: PropertyKey, isStatic: boolean, enumerable: boolean, originalMethod: Function, thisArg: any, args: IArguments, setProto: boolean, makeNonConfigurable: boolean, resultSelector: ResultSelectorFn ): any { const value = originalMethod.apply(thisArg, args) if (resultSelector(value)) { const newDescriptor: PropertyDescriptor = { configurable: !makeNonConfigurable, enumerable, value } if (isStatic || setProto) { Object.defineProperty(target, key, newDescriptor) } if (!isStatic) { Object.defineProperty(thisArg, key, newDescriptor) } } return value } /** * Evaluate the getter function and cache the result * @param [setProto=false] Set the value on the class prototype as well. Only applies to non-static getters. * @param [makeNonConfigurable=false] Set to true to make the resolved property non-configurable * @param [resultSelector] A filter function that must return true for the value to cached * @return A decorator function */ export function LazyGetter( setProto = false, makeNonConfigurable = false, resultSelector: ResultSelectorFn = defaultFilter ): MethodDecorator & ResettableDescriptor { let desc: PropertyDescriptor let prop: PropertyKey let args: IArguments = null let isLegacy: boolean function decorator( targetOrDesc: any, key: PropertyKey, descriptor: PropertyDescriptor ): DecoratorReturn { // eslint-disable-next-line prefer-rest-params args = arguments if (key === undefined) { if (typeof desc === "undefined") { isLegacy = false prop = ( targetOrDesc).key desc = Object.assign( {}, ( targetOrDesc).descriptor /* istanbul ignore next */ || targetOrDesc ) } return decorateNew(targetOrDesc, setProto, makeNonConfigurable, resultSelector) } else { if (typeof desc === "undefined") { isLegacy = true prop = key desc = Object.assign( {}, descriptor /* istanbul ignore next */ || Object.getOwnPropertyDescriptor( targetOrDesc, key ) ) } return decorateLegacy( targetOrDesc, key, descriptor, setProto, makeNonConfigurable, resultSelector ) } } decorator.reset = setProto ? thrower : (on: any): void => { if (!on) { throw new Error("Unable to restore descriptor on an undefined target") } if (!desc) { throw new Error( "Unable to restore descriptor. Did you remember to apply your decorator to a method?" ) } // Restore descriptor to its original state Object.defineProperty(on, prop, desc) // eslint-disable-next-line prefer-spread const ret: any = decorator.apply(null, args) Object.defineProperty(on, prop, isLegacy ? ret : ret.descriptor || ret) } return decorator } function thrower(): never { throw new Error("This decoration modifies the class prototype and cannot be reset.") } export type RefinementWithIndex = (i: I, a: A) => a is B export type PredicateWithIndex = (i: I, a: A) => boolean export type Erase = R & K extends K & infer R1 ? R1 : R /** from ts-toolbelt, minimal port of Compute */ export type Depth = "flat" | "deep" type Errors = Error // | EvalError // | RangeError // | ReferenceError // | SyntaxError // | TypeError // | URIError type Numeric = // | Number // | BigInt // not needed // | Math Date type Textual = // | String RegExp type Arrays = // | Array // | ReadonlyArray | Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array // | BigInt64Array // | BigUint64Array type Maps = // | Map // | Set | ReadonlyMap | ReadonlySet | WeakMap | WeakSet type Structures = | ArrayBuffer // | SharedArrayBuffer // | Atomics | DataView // | JSON type Abstractions = Function | Promise | Generator // | GeneratorFunction type WebAssembly = never export type BuiltInObject = | Errors | Numeric | Textual | Arrays | Maps | Structures | Abstractions | WebAssembly export type ComputeRaw = A extends Function ? A : & { [K in keyof A]: A[K] } & {} export type ComputeFlat = A extends BuiltInObject ? A : & { [K in keyof A]: A[K] } & {} export type ComputeDeep = A extends BuiltInObject ? A : & { [K in keyof A]: ComputeDeep } & {} export type Compute = { flat: ComputeFlat deep: ComputeDeep }[depth] ///// export const LazySymbol = Symbol("lazy") interface Lazy { [LazySymbol]: Record } export function lazyGetter(creator: (target: T) => T2) { const key = Symbol(creator.name) const f = (target: T): T2 => { let lazy = (target as unknown as Lazy)[LazySymbol] if (!lazy) { lazy = {} Object.defineProperty(target, LazySymbol, { enumerable: false, value: lazy }) } else if (lazy[key]) { return lazy[key] } const value = creator(target) lazy[key] = value return value } Object.defineProperty(f, "name", { enumerable: false, value: `Lazy<${creator.name}>` }) return f } export function exhaustiveMatch() { return any>>(handlers: Out) => (t: T): ReturnType => handlers[t](t) } export function exhaustiveMatch_(t: T) { return any>>(handlers: Out): ReturnType => handlers[t](t) } export function assertUnreachable(x: never): never { throw new Error("Unknown case " + x) } export type OptPromise any> = ( ...args: Parameters ) => Promise> | ReturnType export function access(t: Record) { return (key: T) => t[key] } export function todayAtUTCNoon() { const localDate = new Date() const utcDateAtNoon = Date.UTC( localDate.getFullYear(), localDate.getMonth(), localDate.getDate(), 12 ) return new Date(utcDateAtNoon) } function anyOp_$(self: T) { return { get subject() { return self } } } export function anyOp$(self: T): AnyOps { return anyOp_$(self) } export interface AnyOps { subject: T } export const clone = dual< (f: NoInfer) => (self: A) => A, (self: A, f: A) => A >(2, (self, f) => { if (cloneTrait in (self as any)) { const selfWithClone = self as typeof self & Clone return selfWithClone[cloneTrait](f) } return Object.setPrototypeOf(f, Object.getPrototypeOf(self)) as typeof self }) export const copy = dual< { (f: (a: A) => Partial>): (self: A) => A (f: Partial>): (self: A) => A }, { (self: A, f: (a: A) => Partial): A (self: A, f: Partial): A } >(2, (self: A, f: Partial | ((a: A) => Partial)) => clone(self, { ...self, ...(isFunction(f) ? f(self) : f) })) export type CopyOriginU any> = & { [K in keyof U & keyof InstanceType]?: U[K] } & {} export type CopyOriginRet = & { [K in keyof A | keyof U]: K extends keyof U ? U[K] : A[K & keyof A] } & {} export type CopyOriginSelf = Equals<{}, U> extends true ? Equals extends true ? `updates argument is empty or contains only extra properties` : A : A export interface StructuralCopyOrigin { >( f: (a: A) => & { [K in keyof U & keyof Self]?: U[K] } & {} ): (self: CopyOriginSelf) => CopyOriginRet >( updates: & { [K in keyof U & keyof Self]?: U[K] } & {} ): (self: CopyOriginSelf) => CopyOriginRet >( self: CopyOriginSelf, f: (a: A) => & { [K in keyof U & keyof Self]?: U[K] } & {} ): CopyOriginRet >( self: CopyOriginSelf, updates: & { [K in keyof U & keyof Self]?: U[K] } & {} ): CopyOriginRet } // just one input param: the convention is that the ctor takes an object // containing the properties of the value (I can't put object there as type because of contravariance) /** * By design this does not return `Self` directly. * * The return type is computed from `Self` and the update payload so callers can * expose an explicit structural return type that remains assignable to `Self`. */ export const copyOrigin = any>(ctor: Ctor) => dual< { , U extends Partial>>( f: (a: A) => CopyOriginU ): (self: CopyOriginSelf) => CopyOriginRet , U extends Partial>>( updates: CopyOriginU ): (self: CopyOriginSelf) => CopyOriginRet }, { , U extends Partial>>( self: CopyOriginSelf, f: (a: A) => CopyOriginU ): CopyOriginRet , U extends Partial>>( self: CopyOriginSelf, updates: CopyOriginU ): CopyOriginRet } >( 2, , U extends Partial>>( self: Equals<{}, U> extends true ? `updates argument is empty or contains only extra properties` : A, f: | CopyOriginU | ((a: A) => CopyOriginU) ): CopyOriginRet => { const o = { ...self, ...(isFunction(f) ? f(self as any) : f) } if (cloneTrait in (self as any)) { const selfWithClone = self as typeof self & Clone return selfWithClone[cloneTrait](o) } return new ctor(o) } ) export function debug(a: AnyOps, name: string) { let r: string | A = a.subject try { r = pretty(a.subject) } catch { /* empty */ } return Effect .logDebug("print") .pipe( Effect.annotateLogs(name, `${r}`), Effect .map(() => a.subject) ) } export function debugUnsafe(a: AnyOps, name: string) { console.log(name, a.subject) return a.subject } export function spread< // eslint-disable-next-line @typescript-eslint/no-explicit-any Fields extends Record, // eslint-disable-next-line @typescript-eslint/no-explicit-any NewProps >(fields: Fields, fnc: (fields: Fields) => NewProps) { return fnc(fields) } export function spreadS< // eslint-disable-next-line @typescript-eslint/no-explicit-any Fields extends Record >(fields: Fields, fnc: (fields: Fields) => Fields) { return fnc(fields) } export function makeAzureFriendly(path: string) { return path.replace(/\//g, "___SL@SH___") } export function undoAzureFriendly(path: T): T { return path.replace(/___SL@SH___/g, "/") as T } export function arrayMove( arrInput: readonly T[], oldIndex: number, newIndex: number ) { const arr: (T | undefined)[] = [...arrInput] while (oldIndex < 0) { oldIndex += arr.length } while (newIndex < 0) { newIndex += arr.length } if (newIndex >= arr.length) { let k = newIndex - arr.length + 1 while (k--) { arr.push(undefined) } } arr.splice(newIndex, 0, arr.splice(oldIndex, 1)[0]) return arr } export function arrayMoveDropUndefined( arrInput: readonly (T | undefined)[], oldIndex: number, newIndex: number ): T[] { return arrayMove(arrInput, oldIndex, newIndex).filter((x): x is T => x !== undefined) } export function arMoveElDropUndefined(el: T, newIndex: number) { return (arrInput: ReadonlyArray): Option.Option> => { const ar = [...arrInput] const index = ar.findIndex((x) => x === el) if (index === -1) { return Option.none() } return Option.some(arrayMoveDropUndefined(ar, index, newIndex)) } } export function setMoveElDropUndefined(el: T, newIndex: number) { return (arrInput: ReadonlySet): Option.Option> => pipe([...arrInput], arMoveElDropUndefined(el, newIndex), Option.map((ar) => new Set(ar))) } type RemoveNonArray = T extends readonly any[] ? T : never export function isNativeTuple(a: A): a is RemoveNonArray { return Array.isArray(a) } export const mutable: { (a: A, deep: true): DeepMutable; (a: A): Mutable } = identity /** * Recursively removes all elements assignable to type `E` from tuple `T`. * * @example * ```typescript * type Mixed = [1, "hello", 2, "world", 3] * type Numbers = ExcludeFromTuple * // Result: [1, 2, 3] * ``` */ export type ExcludeFromTuple = T extends [infer F, ...infer R] ? [F] extends [E] ? ExcludeFromTuple : [F, ...ExcludeFromTuple] : [] export const addAbortToRuntimeFiber = (fiber: Fiber.Fiber, signal: AbortSignal) => { const abort = () => Effect.runSync(Fiber.interrupt(fiber)) if (signal.aborted) { abort() return fiber } signal.addEventListener("abort", abort) return fiber } export const runtimeFiberAsPromise = (fiber: Fiber.Fiber, signal?: AbortSignal) => { if (signal) addAbortToRuntimeFiber(fiber, signal) return new Promise((resolve, reject) => fiber.addObserver((exit) => { if (Exit.isSuccess(exit)) { resolve(exit.value) } else { // eslint-disable-next-line reject(Cause.squash(exit.cause)) } }) ) } // unifies any input to be an effect. export const wrapEffect = >( m: ((...args: Args) => A) | ((...args: Args) => Effect.Effect) | I ) => { if (typeof m === "function") { return (...args: Args): Effect.Effect => { const r = (m as any)(...args) if (Effect.isEffect(r)) return r as Effect.Effect return Effect.succeed(r) } } return (): Effect.Effect => Effect.succeed(m) } export type UnionToTuples = [T] extends [never] ? [] : T extends any ? | [T, ...UnionToTuples>] | UnionToTuples> : [] const genConstructor = (function*() {}).constructor /** * @example * ```ts * import * as Utils from "effect/Utils" * * function* generatorFn() { * yield 1 * yield 2 * } * * console.log(Utils.isGeneratorFunction(generatorFn)) // true * console.log(Utils.isGeneratorFunction(() => {})) // false * ``` * * @category predicates * @since 3.11.0 */ export const isGeneratorFunction = (u: unknown): u is (...args: Array) => Generator => isFunction(u) && u.constructor === genConstructor