import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; export interface NamespaceNode { Id: string; Parent?: NamespaceNode; } export class Namespace extends Context.Service()( "Alchemy/Namespace", ) {} export function push( id: Id, eff: Effect.Effect, ): Effect.Effect; export function push( id: Id, ): ( eff: Effect.Effect, ) => Effect.Effect; export function push(id: string, eff?: Effect.Effect) { return eff ? Effect.flatMap(CurrentNamespace, (parent) => Effect.provideService(eff, Namespace, { Id: id, Parent: parent, }), ) : (eff: Effect.Effect) => push(id, eff); } export const set = (namespace: string | NamespaceNode) => Effect.provideService( Namespace, typeof namespace === "string" ? { Id: namespace } : namespace, ); export const CurrentNamespace = Effect.serviceOption(Namespace) .asEffect() .pipe(Effect.map(Option.getOrUndefined)); export const CurrentChain = CurrentNamespace.asEffect().pipe( Effect.map(function findRoot(ns): string[] { if (ns?.Parent) { return [ns.Id, ...findRoot(ns.Parent)]; } return ns ? [ns.Id] : []; }), ); export const Parent = Namespace.asEffect().pipe(Effect.map((ns) => ns?.Parent)); export const Root = Namespace.asEffect().pipe( Effect.map(function findRoot(ns) { if (ns.Parent) { return findRoot(ns.Parent); } return ns; }), );