/** * Originally ported from https://github.com/zio/zio/blob/master/core/shared/src/main/scala/zio/Has.scala * by Michael Arnaldi and the Matechs Garage Contributors * * Copyright 2017-2020 John A. De Goes and the ZIO Contributors * * Copyright 2020 Michael Arnaldi and the Matechs Garage Contributors. * * Derived from the above sources */ import type { Option } from "../Option"; import { fromNullable, none } from "../Option"; /** * URI used in Has */ export declare const HasURI: unique symbol; /** * Has signal presence of a specific service provided via Tag in the environment */ export interface Has { [HasURI]: { _T: () => T; }; } /** * URI used in Region */ export const RegionURI: unique symbol = Symbol(); /** * Branding sub-environments */ export interface Region { [RegionURI]: { _K: () => K; _T: () => T; }; } /** * Extract the type of a class constructor */ export type ConstructorType> = K extends { prototype: infer T; } ? T : never; export type Constructor = Function & { prototype: T }; /** * Tag Encodes capabilities of reading and writing a service T into a generic environment */ export interface Tag { _tag: "Tag"; _T: T; key: PropertyKey; def: boolean; overridable: () => Tag; fixed: () => Tag; refine: () => Tag; read: (r: Has) => T; readOption: (r: unknown) => Option; setKey: (s: PropertyKey) => Tag; of: (_: T) => Has; } /** * Extract the Has type from any augumented variant */ export type HasTag = [T] extends [Tag] ? Has : never; const makeTag = (def = false, key: PropertyKey = Symbol()): Tag => ({ _tag: "Tag", _T: undefined as any, key, def, of: (t) => ({ [key]: t } as any), overridable: () => makeTag(true, key), fixed: () => makeTag(false, key), refine: () => makeTag(def, key), read: (r: Has) => r[key], readOption: (r) => (typeof r === "object" && r !== null ? fromNullable(r[key]) : none()), setKey: (s: PropertyKey) => makeTag(def, s) }); /** * Create a service entry Tag from a type and a URI */ export function tag>(_: T): Tag>; export function tag(): Tag; export function tag(_?: any): Tag { return makeTag(); } /** * Get the service type of a Has */ export type ServiceType = [T] extends [Has] ? A : never; /** * Replaces the service with the required Service Entry, in the specified environment */ export const replaceServiceIn = (_: Tag, f: (t: T) => T) => (r: R & Has): R & Has => ({ ...r, [_.key]: f(r[_.key]) }); /** * Replaces the service with the required Service Entry, in the specified environment */ export const replaceServiceIn_ = (r: R & Has, _: Tag, f: (t: T) => T): R & Has => ({ ...r, [_.key]: f(r[_.key]) } as any); /** * Flags the current Has to be overridable, when this is used subsequently provided * environments will override pre-existing. Useful to provide defaults. */ export const overridable = (h: Tag): Tag => ({ ...h, def: true }); export function mergeEnvironments(_: Tag, r: R1, t: T): R1 & Has { return _.def && r[_.key] ? r : ({ ...r, [_.key]: t } as any); } export class DerivationContext { readonly hasMap = new Map, Tag>(); derive(has: Tag, f: () => Tag): Tag { const inMap = this.hasMap.get(has); if (inMap) { return inMap; } const computed = f(); this.hasMap.set(has, computed); return computed; } }