/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable import/namespace */ import * as CTX from "effect/Context" import type * as Effect from "effect/Effect" import * as Layer from "effect/Layer" import type * as Scope from "effect/Scope" import type * as Types from "effect/Types" export * from "effect/Context" export interface Opaque extends CTX.Key { of(this: void, self: Shape): Self context(self: Shape): CTX.Context // a version that leverages the Shape -> Self conversion toLayer: ( eff: Effect.Effect ) => Layer.Layer> use(f: (service: Shape) => Effect.Effect): Effect.Effect useSync(f: (service: Shape) => A): Effect.Effect } // export interface OpaqueMake // extends CTX.Service // { // // temp while sorting out https://github.com/Effect-TS/effect-smol/pull/1534 // of(self: Shape): Self // contextMap2(self: Shape): CTX.Context // // a version that leverages the Shape -> Self conversion // toLayer: { // ( // eff: Effect.Effect // ): Layer.Layer> // (): Layer.Layer> // } // } export function assignTag( key: string, creationError?: Error ) { return (cls: S): S & Opaque => { const tag = CTX.Service(key) let fields = tag if (Reflect.ownKeys(cls).includes("key")) { const { key, ...rest } = tag fields = rest as any } const t = Object.assign(cls, Object.getPrototypeOf(tag), fields) if (!creationError) { const limit = Error.stackTraceLimit Error.stackTraceLimit = 2 creationError = new Error() Error.stackTraceLimit = limit } // the stack is used to get the location of the tag definition, if a service is not found in the registry Object.defineProperty(t, "stack", { get() { return creationError!.stack } }) return t } } /** Accessor for a service method that returns a plain value. Wraps via `useSync`. */ export const accessFn = < Self extends object, Shape extends Record, K extends keyof Shape >( Tag: Opaque, key: K ): Shape[K] extends (...args: [...infer Args]) => infer A ? (...args: Readonly) => Effect.Effect : never => ((...args: Array) => Tag.useSync((s: any) => s[key](...args))) as any /** Accessor for a service method that returns an Effect. Delegates via `use`. */ export const accessEffectFn = < Self extends object, Shape extends Record, K extends keyof Shape >( Tag: Opaque, key: K ): Shape[K] extends (...args: [...infer Args]) => Effect.Effect ? (...args: Readonly) => Effect.Effect : never => ((...args: Array) => Tag.use((s: any) => s[key](...args))) as any /** Accessor for a service property (constant). Wraps via `useSync`. */ export const accessCn = < Self extends object, Shape extends Record, K extends keyof Shape >( Tag: Opaque, key: K ): Effect.Effect => Tag.useSync((s) => s[key]) /** Accessor for a service property that is an Effect. Delegates via `use`. */ export const accessEffectCn = < Self extends object, Shape extends Record, K extends keyof Shape >( Tag: Opaque, key: K ): Shape[K] extends Effect.Effect ? Effect.Effect : never => Tag.use((s: any) => s[key]) as any export const TypeId = "~Context.Opaque" // export function Opaque(key: Key) { // return () => { // const limit = Error.stackTraceLimit // Error.stackTraceLimit = 2 // const creationError = new Error() // Error.stackTraceLimit = limit // const c: abstract new(_: never) => Shape & { readonly [TypeId]: Key } = class {} as any // return assignTag(key, creationError)(c) // } // } export interface OpaqueClass extends Opaque { new(_: never): Shape & { readonly [TypeId]: Identifier } readonly key: Identifier } // export interface OpaqueClassMake // extends OpaqueMake // { // new(_: never): Shape & { readonly [TypeId]: Identifier } // readonly key: Identifier // } export const Opaque: { (): < const Identifier extends string, E, R = Types.unassigned, Args extends ReadonlyArray = never >( id: Identifier, options?: { readonly make: ((...args: Args) => Effect.Effect) | Effect.Effect | undefined } ) => & OpaqueClass & ([Types.unassigned] extends [R] ? unknown : { readonly make: [Args] extends [never] ? Effect.Effect : (...args: Args) => Effect.Effect }) (): < const Identifier extends string, Make extends Effect.Effect | ((...args: any) => Effect.Effect) >( id: Identifier, options: { readonly make: Make } ) => & OpaqueClass< Self, Identifier, Make extends | Effect.Effect | ((...args: infer _Args) => Effect.Effect) ? _A : never > & { readonly make: Make } } = () => (id: string, options: any) => { const svc = CTX.Service()(id, options) as any return Object.assign(svc, { toLayer: (eff: Effect.Effect) => { return Layer.effect(svc, eff) } }) }