import { inspect } from 'node:util'; class Item_Unresolvable extends Error { constructor(data) { const { resolver, item } = data; const type = item === null ? 'null' : typeof item; super(`Cannot resolve item ${inspect(item)} of type "${type}" using resolver ${resolver}`); this.data = data; } } class Abstract_Resolver { resolve(item) { const handler = this.resolve_handler(item); if (!handler) { throw new Item_Unresolvable({ resolver: this, item }); } return handler({ resolver: this, item }); } } class Chained_Resolver extends Abstract_Resolver { constructor(chain_links) { super(); Object.assign(this, { chain_links }); } resolve_handler(item) { const { chain_links } = this; for (const link of chain_links) { const handler = link.resolve_handler(item); if (handler) { return handler; } } } } class Mapping_Resolver { constructor(rules=new Map(), key_function=null) { Object.assign(this, { rules, key_function }); } resolve_handler(item) { const { key_function, rules } = this; const key = key_function ? key_function(item) : item; return rules.get(key); } } const vr = new Mapping_Resolver(); const tr = new Mapping_Resolver(new Map(), item => typeof item); const cr = new Chained_Resolver([vr, tr]); vr.rules.set('HELLO', () => 'WORLD'); tr.rules.set('string', () => 'World'); console.log(cr.resolve('HELLO')); console.log(cr.resolve('hello'));