{"version":3,"file":"echo.cjs","sources":["../packages/echo/interfaces.js","../packages/echo/target.js","../packages/echo/echo.js"],"sourcesContent":["/**\n * Unique identifier for the callback executed when connecting an Echo arc.\n *\n * @const {symbol}\n * @description\n * Used internally by the `Echo` mixin to configure listeners associated\n * with the `on` attribute. This callback is called when the `on` attribute is added or modified,\n * allowing the registration of new declarative arcs (events arriving at the component).\n */\nexport const connectArc = Symbol.for(\"connectArc\");\n\n/**\n * Unique identifier for the callback executed when disconnecting an Echo arc.\n *\n * @const {symbol}\n * @description\n * Used internally by the `Echo` mixin to remove event listeners\n * registered via the `on` attribute. It's called when removing the value of the `on` attribute or\n * when the component is disconnected from the DOM.\n */\nexport const disconnectArc = Symbol.for(\"disconnectArc\");\n\n/**\n * Unique identifier for the `on` attribute, used to declare reactive listeners.\n *\n * @const {symbol}\n * @description\n * Used to indicate that the `Echo` mixin should observe changes to the `on` attribute,\n * which defines routing protocols for external events to internal destinations\n * (attributes, setters, or methods). Each value of `on` represents a declarative graph.\n */\nexport const on = \"on\";\n","/**\n * Canal global de eventos utilizado pelo mixin `Echo`.\n *\n * @const {EventTarget}\n *\n * @description\n * O `target` atua como um barramento de eventos interno entre componentes personalizados.\n * Ele é usado pelo mixin `Echo` para despachar e ouvir eventos com base nos protocolos\n * declarados no atributo `on`.\n *\n * Ao invés de registrar eventos diretamente no `document` ou em elementos específicos,\n * o `Echo` utiliza esta instância única de `EventTarget` para garantir isolamento e performance.\n *\n * Esse canal também facilita a composição entre componentes sem acoplamento direto.\n *\n * @example\n * target.addEventListener(\"deck/review:method>markCorrect\", (event) => {\n *   // processa valor transformado e repassa para o método\n * });\n */\nconst target = new EventTarget();\n\nexport default target;\n","import spark from \"@spark\";\nimport { connectArc, disconnectArc, on } from \"./interfaces\";\nimport target from \"./target\";\n\n/**\n * Echo Mixin\n *\n * @param {CustomElementConstructor} Klass - The base class (usually HTMLElement).\n * @returns {Klass} A new class extended with support for the `on` attribute.\n *\n * @description\n * The `Echo` mixin adds support for the `on` attribute, allowing external events\n * to be mapped to attributes, methods, or setters of the component itself.\n *\n * It observes the `on` attribute, listens to events in the DOM scope, and performs\n * declarative routing via arcs in the format: `source/event:type/sink|sparks`.\n *\n * Filters (sparks) are applied before assigning the value to the destination.\n * This mixin is non-opinionated, compatible with the native lifecycle and other decorators.\n *\n * @example\n * class MyElement extends Echo(HTMLElement) {\n *   set color(value) {\n *     this.style.color = value;\n *   }\n * }\n */\nconst Echo = (Klass) => {\n  class Host extends Klass {\n    #controllers = {};\n\n    /**\n     * Defines the attributes observed by the component, including `on`.\n     * This allows dynamically reacting to changes in the `on` attribute and\n     * registering or removing declared arcs.\n     */\n    static observedAttributes = [...(Klass.observedAttributes ?? []), on];\n\n    /**\n     * Intercepts changes to observed attributes.\n     * In the case of Echo, handles the activation/deactivation of arcs declared via `on`.\n     *\n     * @param {string} name - Name of the changed attribute.\n     * @param {string|null} oldValue - Previous attribute value.\n     * @param {string|null} newValue - New attribute value.\n     * @returns {this}\n     */\n    attributeChangedCallback(name, oldValue, newValue) {\n      super.attributeChangedCallback?.(name, oldValue, newValue);\n\n      if (name === on) {\n        this[disconnectArc](oldValue);\n        this[connectArc](newValue);\n      }\n\n      return this;\n    }\n\n    /**\n     * Terminates all active arcs when the element is removed from the DOM.\n     *\n     * @returns {this}\n     */\n    disconnectedCallback() {\n      super.disconnectedCallback?.();\n\n      for (const arc of Object.keys(this.#controllers)) {\n        this[disconnectArc](arc);\n      }\n\n      return this;\n    }\n\n    /**\n     * Dispatches a custom event so that other Echo components can react.\n     *\n     * The event name is kept (without prefix) and the `detail` payload includes:\n     *\n     * - `attribute.id`: value of the component's `id` attribute.\n     * - `attribute.name`: value of the `name` attribute.\n     * - `node`: tag name (`localName`).\n     * - `token`: original content of the received `event.detail`.\n     *\n     * This structure standardizes the source metadata for filtering by listeners.\n     *\n     * @param {CustomEvent} event - Event to be dispatched.\n     */\n    dispatchEvent(event) {\n      super.dispatchEvent?.(event);\n\n      target.dispatchEvent(\n        new CustomEvent(event.type, {\n          detail: {\n            attribute: {\n              id: this.getAttribute(\"id\"),\n              name: this.getAttribute(\"name\"),\n            },\n            node: this.localName,\n            token: event.detail,\n          },\n        }),\n      );\n    }\n\n    /**\n     * Interprets and executes an arc defined in the `on` attribute.\n     *\n     * An arc defines:\n     * - `source`: event origin (`id`, `name`, or `tag`).\n     * - `event`: event type.\n     * - `type`: execution type (method, setter, or attribute).\n     * - `sink`: destination in the component.\n     * - `filters`: transformations applied to the value before reaching the destination.\n     *\n     * @param {string} arc - Arc in the format `source/event:type/sink|filters`.\n     * @returns {this}\n     */\n    [connectArc](arc) {\n      this.#controllers[arc] = new AbortController();\n\n      const [, source, event, type, sink, filters] =\n        arc.match(/^([*#\\w-]+)\\/([\\w-]+):([a-z]+)\\/([\\w-]+)(?:\\|(.*))?$/i) ||\n        [];\n\n      const transforms = (filters || \"\")\n        .split(\"|\")\n        .filter(Boolean)\n        .map((filter) => filter.split(\"=\"))\n        .map(([name, value]) => [spark.get(name), value]);\n\n      target.addEventListener(\n        event,\n        (e) => {\n          const {\n            attribute: { id, name },\n            node,\n            token,\n          } = e.detail;\n\n          if (new RegExp(`^(\\\\*|#${id}|${name}|${node})$`, \"i\").test(source)) {\n            const payload = transforms.reduce(\n              (data, [fn, value]) => fn(data, value),\n              token,\n            );\n\n            if (/method$/i.test(type)) this[sink]?.(payload);\n            if (/attribute$/i.test(type)) this.setAttribute(sink, payload);\n            if (/setter$/i.test(type)) this[sink] = payload;\n          }\n\n          return this;\n        },\n        {\n          signal: this.#controllers[arc].signal,\n        },\n      );\n\n      return this;\n    }\n\n    /**\n     * Removes the listener associated with a specific arc using AbortController.\n     *\n     * @param {string} arc - Arc that will be disconnected.\n     * @returns {this}\n     */\n    [disconnectArc](arc) {\n      this.#controllers[arc]?.abort();\n      return this;\n    }\n  }\n\n  return Host;\n};\n\nexport default Echo;\n"],"names":["connectArc","Symbol","for","disconnectArc","target","EventTarget","Klass","Host","controllers","static","observedAttributes","name","oldValue","newValue","super","attributeChangedCallback","this","disconnectedCallback","arc","Object","keys","event","dispatchEvent","CustomEvent","type","detail","attribute","id","getAttribute","node","localName","token","AbortController","source","sink","filters","match","transforms","split","filter","Boolean","map","value","spark","get","addEventListener","e","RegExp","test","payload","reduce","data","fn","setAttribute","signal","abort"],"mappings":";;kCASaA,IAAaC,OAAOC,IAAI,YAAA,GAWxBC,IAAgBF,OAAOC,IAAI,eAAA,GCAlCE,IAAS,IAAIC;kBCOLC,OAAAA;AAAAA,EACZ,MAAMC,UAAaD,EAAAA;AAAAA,IACjBE,KAAe,CAAA;AAAA,IAOfC,OAAAA,qBAA4B,CAAA,GAAKH,EAAMI,sBAAsB,CAAA,GFL/C;IEgBd,yBAAyBC,IAAMC,IAAUC,IAAAA;AAQvC,aAPAC,MAAMC,2BAA2BJ,IAAMC,IAAUC,EAAAA,GFjBrC,SEmBRF,OACFK,KAAKb,GAAeS,EAAAA,GACpBI,KAAKhB,CAAAA,EAAYa,EAAAA,IAGZG;AAAAA,IACT;AAAA,IAOA,uBAAAC;AACEH,YAAMG,uBAAAA;AAEN,iBAAWC,MAAOC,OAAOC,KAAKJ,KAAAA,EAAKR,EACjCQ,MAAKb,CAAAA,EAAee,EAAAA;AAGtB,aAAOF;AAAAA,IACT;AAAA,IAgBA,cAAcK,IAAAA;AACZP,YAAMQ,gBAAgBD,EAAAA,GAEtBjB,EAAOkB,cACL,IAAIC,YAAYF,GAAMG,MAAM,EAC1BC,QAAQ,EACNC,WAAW,EACTC,IAAIX,KAAKY,aAAa,IAAA,GACtBjB,MAAMK,KAAKY,aAAa,MAAA,EAAA,GAE1BC,MAAMb,KAAKc,WACXC,OAAOV,GAAMI,OAAAA,EAAAA,CAAAA,CAAAA;AAAAA,IAIrB;AAAA,IAeA,CAACzB,CAAAA,EAAYkB,IAAAA;AACXF,WAAAA,GAAkBE,EAAAA,IAAO,IAAIc;AAE7B,YAAA,CAAM,EAAGC,IAAQZ,IAAOG,IAAMU,GAAMC,CAAAA,IAClCjB,GAAIkB,MAAM,uDAAA,KACV,CAAA,GAEIC,KAAcF,KAAW,IAC5BG,MAAM,GAAA,EACNC,OAAOC,OAAAA,EACPC,IAAKF,CAAAA,OAAWA,GAAOD,MAAM,GAAA,CAAA,EAC7BG,IAAI,CAAA,CAAE9B,IAAM+B,QAAW,CAACC,EAAAA,QAAMC,IAAIjC,EAAAA,GAAO+B,EAAAA,CAAAA;AA6B5C,aA3BAtC,EAAOyC,iBACLxB,IACCyB,CAAAA,OAAAA;AACC,cAAA,EACEpB,WAAAA,EAAWC,IAAEA,IAAEhB,MAAEA,GAAAA,GAAMkB,MACvBA,IAAIE,OACJA,GAAAA,IACEe,GAAErB;AAEN,YAAI,IAAIsB,OAAO,UAAUpB,EAAAA,IAAMhB,EAAAA,IAAQkB,QAAU,GAAA,EAAKmB,KAAKf,EAAAA,GAAS;AAClE,gBAAMgB,KAAUZ,EAAWa,OACzB,CAACC,IAAAA,CAAOC,IAAIV,EAAAA,MAAWU,GAAGD,IAAMT,KAChCX,EAAAA;AAGE,qBAAWiB,KAAKxB,EAAAA,KAAOR,KAAKkB,CAAAA,IAAQe,EAAAA,GACpC,cAAcD,KAAKxB,EAAAA,KAAOR,KAAKqC,aAAanB,GAAMe,EAAAA,GAClD,WAAWD,KAAKxB,EAAAA,MAAOR,KAAKkB,CAAAA,IAAQe;AAAAA,QAC1C;AAEA,eAAOjC;AAAAA,MAAI,GAEb,EACEsC,QAAQtC,KAAAA,GAAkBE,EAAAA,EAAKoC,OAAAA,CAAAA,GAI5BtC;AAAAA,IACT;AAAA,IAQA,CAACb,CAAAA,EAAee,IAAAA;AAEd,aADAF,KAAAA,GAAkBE,EAAAA,GAAMqC,MAAAA,GACjBvC;AAAAA,IACT;AAAA,EAAA;AAGF,SAAOT;AAAI;"}