phlex-reactive 0.16.8 → 0.16.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -132,10 +132,37 @@ function enqueue(event, el, descs, win, shortcut) {
132
132
  }
133
133
  }
134
134
 
135
+ // Issue #442: an on_client descriptor whose every record on its `on` is a
136
+ // within: delegation the event's target falls outside of (no matching element
137
+ // strictly under `el`: its parent is not inside `el`) is a miss — runOps
138
+ // would run nothing and prevent nothing — so it is neither queued nor
139
+ // prevented: a link in a delegated list's footer still navigates before
140
+ // connect. A record without within:, or an attr that does not parse (a legacy
141
+ // list), keeps the descriptor; a within: that does not parse is a miss, as
142
+ // it is for runOps. (A row of a nested root is still queued; the replay's
143
+ // ownership check makes it a miss.)
144
+ const outside = (el, target, within) => {
145
+ try {
146
+ return !el.contains(target.closest(within)?.parentElement ?? null)
147
+ } catch {
148
+ return true
149
+ }
150
+ }
151
+
152
+ function delegatedMiss(el, d, target) {
153
+ let mine
154
+ try {
155
+ mine = el.dataset.reactiveOpsParam.split(" ").map(JSON.parse).filter((r) => r.on === d.token.split("->")[0])
156
+ } catch {
157
+ return false
158
+ }
159
+ return mine.length > 0 && mine.every((r) => r.within != null && outside(el, target, r.within))
160
+ }
161
+
135
162
  function record(event) {
136
163
  for (let el = event.target; el?.closest; el = el.parentElement) {
137
164
  if (!event.bubbles && el !== event.target) break
138
- const descs = matching(el, event, false)
165
+ const descs = matching(el, event, false).filter((d) => d.method !== "runOps" || !delegatedMiss(el, d, event.target))
139
166
  if (descs.length) enqueue(event, el, descs, false)
140
167
  }
141
168
  }
@@ -1,4 +1,4 @@
1
- var O=Symbol.for("phlex-reactive.early");var m=globalThis[O]??={queue:[],connected:new WeakSet};function k(e){let t=/^(?:(key\w+)\.([\w+]+)|([^@>]+))(@window)?->reactive#(dispatch|runOps)(?::!?\w+)*$/.exec(e);return t?[{token:e,type:t[1]??t[3],filter:t[2],win:!!t[4],method:t[5]}]:[]}function w(e,t){if(!e)return!0;let a=e.split("+"),r=a.pop();return(r==="esc"?"escape":r==="space"?" ":r.replace(/^(?=up|down|left|right)/,"arrow").replace("_",""))===t.key?.toLowerCase()&&["meta","ctrl","alt","shift"].every((s)=>a.includes(s)===t[`${s}Key`])}var y=(e)=>(e.dataset.action??"").split(/\s+/).flatMap(k),g=(e,t,a)=>y(e).filter((r)=>r.win===a&&r.type===t.type&&w(r.filter,t));function b(e,t,a,r,o){let s=t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]');if(!s||m.connected.has(s))return;let c=/^(checkbox|radio)$/.test(t.type)&&/"checked":"keep"/.test(t.dataset.reactiveOptimisticParam),i=` ${t.dataset.reactiveOpsParam??""}`,p=(f)=>{let h=` {"on":${JSON.stringify(f.token.split("->")[0])}`;return f.method==="runOps"?i.includes(h)&&i.split(h).length===i.split(`${h},"prevent":false`).length:c||t.dataset.reactivePreventParam==="false"};if(r?o:!a.every(p)){if(r&&!e.defaultPrevented)(m.prevented??=new WeakSet).add(e);e.preventDefault()}let{queue:d}=m;if(d.push({event:e,el:t,root:s,descs:a,win:r,at:performance.now()})>50)d.shift();let{reactiveDormant:l,controller:n=""}=s.dataset;if(l)s.dataset.controller=`${n} ${l}`.trim(),delete s.dataset.reactiveDormant}function E(e){for(let t=e.target;t?.closest;t=t.parentElement){if(!e.bubbles&&t!==e.target)break;let a=g(t,e,!1);if(a.length)b(e,t,a,!1)}}var u=new Set,S=u;function T(e){if(!e.bubbles&&e.target!==e.currentTarget)return;for(let t of u){if(!t.isConnected){u.delete(t);continue}if(m.connected.has(t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]')))continue;let a=t.dataset.reactiveOpsParam??"",r=t.dataset.reactiveShortcutParam??(/"shortcut":/.test(a)&&a),{target:o}=e,s=r&&!/inEditable":true/.test(r)&&(o.isContentEditable||/^(input|textarea|select)$/.test(o.localName)),c=r&&(/[Mm]ac|iP/.test(navigator.userAgentData?.platform??navigator.platform)?"meta":"ctrl"),i=/mod\+/.test(r)&&(c==="meta"?"ctrl":"meta"),p=r&&r.match(/(^|"(keys|shortcut)":")[\w+]*(delete|backspace)(?="|$)/g)||[],d=(n)=>!n.filter&&p[0]&&n.type==="keydown",l=g(t,e,!0).filter((n)=>(d(n)?!s&&p.some((f)=>w(f.slice(f.lastIndexOf('"')+1).replace("mod",c),e)):!(n.filter&&(s||i&&n.filter.includes(i))))&&(n.method==="runOps"?/"window":true(?!,"outside")/.test(a):t.dataset.reactiveOutsideParam!=="true"));if(l.length)b(e,t,l,!0,r&&l.some((n)=>n.filter||d(n)))}}function v(e=document){let t=(r)=>{if(r.nodeType!==1)return;for(let o of[r,...r.querySelectorAll('[data-action*="reactive#"]')])for(let{type:s,win:c}of y(o)){if(c)u.add(o);(c?e.defaultView:e).addEventListener(s,c?T:E,!0)}},a=()=>{for(let r of u)if(!r.isConnected)u.delete(r)};a(),t(e.documentElement),new MutationObserver((r)=>{a();for(let o of r)o.type==="attributes"?t(o.target):o.addedNodes.forEach(t)}).observe(e.documentElement,{childList:!0,subtree:!0,attributeFilter:["data-action"]})}if(globalThis.document)v();export{S as __windowBoundForTest,v as startEarly};
1
+ var b=Symbol.for("phlex-reactive.early");var m=globalThis[b]??={queue:[],connected:new WeakSet};function k(e){let t=/^(?:(key\w+)\.([\w+]+)|([^@>]+))(@window)?->reactive#(dispatch|runOps)(?::!?\w+)*$/.exec(e);return t?[{token:e,type:t[1]??t[3],filter:t[2],win:!!t[4],method:t[5]}]:[]}function w(e,t){if(!e)return!0;let a=e.split("+"),r=a.pop();return(r==="esc"?"escape":r==="space"?" ":r.replace(/^(?=up|down|left|right)/,"arrow").replace("_",""))===t.key?.toLowerCase()&&["meta","ctrl","alt","shift"].every((n)=>a.includes(n)===t[`${n}Key`])}var y=(e)=>(e.dataset.action??"").split(/\s+/).flatMap(k),g=(e,t,a)=>y(e).filter((r)=>r.win===a&&r.type===t.type&&w(r.filter,t));function O(e,t,a,r,s){let n=t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]');if(!n||m.connected.has(n))return;let c=/^(checkbox|radio)$/.test(t.type)&&/"checked":"keep"/.test(t.dataset.reactiveOptimisticParam),i=` ${t.dataset.reactiveOpsParam??""}`,p=(f)=>{let h=` {"on":${JSON.stringify(f.token.split("->")[0])}`;return f.method==="runOps"?i.includes(h)&&i.split(h).length===i.split(`${h},"prevent":false`).length:c||t.dataset.reactivePreventParam==="false"};if(r?s:!a.every(p)){if(r&&!e.defaultPrevented)(m.prevented??=new WeakSet).add(e);e.preventDefault()}let{queue:d}=m;if(d.push({event:e,el:t,root:n,descs:a,win:r,at:performance.now()})>50)d.shift();let{reactiveDormant:l,controller:o=""}=n.dataset;if(l)n.dataset.controller=`${o} ${l}`.trim(),delete n.dataset.reactiveDormant}var E=(e,t,a)=>{try{return!e.contains(t.closest(a)?.parentElement??null)}catch{return!0}};function v(e,t,a){let r;try{r=e.dataset.reactiveOpsParam.split(" ").map(JSON.parse).filter((s)=>s.on===t.token.split("->")[0])}catch{return!1}return r.length>0&&r.every((s)=>s.within!=null&&E(e,a,s.within))}function T(e){for(let t=e.target;t?.closest;t=t.parentElement){if(!e.bubbles&&t!==e.target)break;let a=g(t,e,!1).filter((r)=>r.method!=="runOps"||!v(t,r,e.target));if(a.length)O(e,t,a,!1)}}var u=new Set,x=u;function S(e){if(!e.bubbles&&e.target!==e.currentTarget)return;for(let t of u){if(!t.isConnected){u.delete(t);continue}if(m.connected.has(t.closest('[data-controller~="reactive"],[data-reactive-dormant~="reactive"]')))continue;let a=t.dataset.reactiveOpsParam??"",r=t.dataset.reactiveShortcutParam??(/"shortcut":/.test(a)&&a),{target:s}=e,n=r&&!/inEditable":true/.test(r)&&(s.isContentEditable||/^(input|textarea|select)$/.test(s.localName)),c=r&&(/[Mm]ac|iP/.test(navigator.userAgentData?.platform??navigator.platform)?"meta":"ctrl"),i=/mod\+/.test(r)&&(c==="meta"?"ctrl":"meta"),p=r&&r.match(/(^|"(keys|shortcut)":")[\w+]*(delete|backspace)(?="|$)/g)||[],d=(o)=>!o.filter&&p[0]&&o.type==="keydown",l=g(t,e,!0).filter((o)=>(d(o)?!n&&p.some((f)=>w(f.slice(f.lastIndexOf('"')+1).replace("mod",c),e)):!(o.filter&&(n||i&&o.filter.includes(i))))&&(o.method==="runOps"?/"window":true(?!,"outside")/.test(a):t.dataset.reactiveOutsideParam!=="true"));if(l.length)O(e,t,l,!0,r&&l.some((o)=>o.filter||d(o)))}}function P(e=document){let t=(r)=>{if(r.nodeType!==1)return;for(let s of[r,...r.querySelectorAll('[data-action*="reactive#"]')])for(let{type:n,win:c}of y(s)){if(c)u.add(s);(c?e.defaultView:e).addEventListener(n,c?S:T,!0)}},a=()=>{for(let r of u)if(!r.isConnected)u.delete(r)};a(),t(e.documentElement),new MutationObserver((r)=>{a();for(let s of r)s.type==="attributes"?t(s.target):s.addedNodes.forEach(t)}).observe(e.documentElement,{childList:!0,subtree:!0,attributeFilter:["data-action"]})}if(globalThis.document)P();export{x as __windowBoundForTest,P as startEarly};
2
2
 
3
- //# debugId=E569865D485AB4CD64756E2164756E21
3
+ //# debugId=B1FCA9A2D1DB67F064756E2164756E21
4
4
  //# sourceMappingURL=early.min.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["early.js"],
4
4
  "sourcesContent": [
5
- "// phlex/reactive/early — never lose a trigger that fires before the `reactive`\n// controller connects (issue #273).\n//\n// An on()/on_client trigger is a Stimulus action: its listener only exists\n// once the controller module has loaded and connected. With a lazily loaded\n// controller (stimulus-loading's lazyLoadControllersFrom, a preload: false\n// pin, a slow network) a click or custom event in that window used to vanish:\n// no request, no error, a UI waiting forever. Import this tiny module EAGERLY\n// (`import \"phlex/reactive/early\"` in the entry point; the engine pins it with\n// preload: true) and the controller can load whenever it likes.\n//\n// How: one CAPTURE listener per trigger event type on document (the types come\n// from the `->reactive#dispatch` / `->reactive#runOps` descriptors in the page,\n// rescanned by a MutationObserver as markup arrives). When an event reaches a\n// descriptor whose root has NOT connected, it is queued and — synchronously,\n// while preventDefault still works — its native default is stopped exactly\n// where dispatch()/runOps() would stop it. connect() drains the queue\n// (reactive_controller.js #drainEarly) and replays each entry.\n//\n// The queue lives on window under a Symbol.for key, so neither module imports\n// the other and either may load first. NO Stimulus import: this module must\n// stay tiny (its gzipped size is asserted by spec/javascript/reactive_early.test.js).\n//\n// Dormant roots (issue #274): reactive_root(dormant: true) renders\n// data-reactive-dormant=\"reactive\" INSTEAD of data-controller=\"reactive\", so\n// nothing mounts (or, with a lazily loaded controller, is even fetched) until\n// one of the root's triggers fires. The first recorded trigger WAKES the root:\n// the identifier moves into data-controller, Stimulus connects it, and the\n// trigger is replayed like any other early event. A dormant root therefore\n// NEEDS this module — without it nothing ever wakes it.\n//\n// Window-bound triggers (window:, issue #303 — how a hotkey is bound) are\n// recorded too, by a capture listener on window: the keypress lands anywhere\n// on the page, so the scan keeps a registry of the elements carrying an\n// `@window` descriptor instead of walking up from the target. They are never\n// prevented (dispatch() does not prevent a window binding either) and live a\n// short TTL in the controller (a hotkey replayed seconds late is wrong).\n// A shortcut: (issue #357) is the exception: the chord is prevented, as\n// dispatch() prevents it, and the other platform's `mod` twin or a press in an\n// editable field is neither queued nor prevented.\n//\n// Limits: outside: triggers and @document bindings are not recorded (an\n// outside click before connect has nothing to close); a key filter is matched\n// against Stimulus's DEFAULT key mappings only.\n\nconst KEY = Symbol.for(\"phlex-reactive.early\")\n\n// The roots whose triggers are captured. A root counts as connected only\n// through state.connected (a WeakSet the controller fills in connect()), never\n// through markup — a Turbo snapshot clone carries data-reactive-connected over.\n// A dormant root (issue #274) is one too: it has no controller yet by design.\nconst ROOT_SELECTOR = '[data-controller~=\"reactive\"],[data-reactive-dormant~=\"reactive\"]'\n\nconst state = (globalThis[KEY] ??= { queue: [], connected: new WeakSet() })\n\n// One Stimulus descriptor token → [{ token, type, filter, win, method }] for a\n// reactive trigger, else [] (flatMap-ready). `win` marks an `@window` token;\n// `@document` never matches. Like Stimulus, a dot suffix is a key filter only\n// on a key event (`keydown.enter`); otherwise it is part of the event name\n// (`panel.opened`).\nfunction parseDescriptor(token) {\n const match = /^(?:(key\\w+)\\.([\\w+]+)|([^@>]+))(@window)?->reactive#(dispatch|runOps)(?::!?\\w+)*$/.exec(token)\n return match ? [{ token, type: match[1] ?? match[3], filter: match[2], win: !!match[4], method: match[5] }] : []\n}\n\n// Stimulus's key-filter rule against its DEFAULT mappings (esc, space, the\n// arrows, page_up/down; letters, digits, enter, tab, home, end map to\n// themselves): the key after the last \"+\" matches event.key, and the four\n// modifiers match exactly (ctrl+enter is not enter). It decides what to queue\n// and preventDefault before connect; the replay re-checks the filter against\n// the app's own key mappings (reactive_controller.js #replayEarly).\nfunction keyMatches(filter, event) {\n if (!filter) return true\n const parts = filter.split(\"+\")\n const key = parts.pop()\n const name = key === \"esc\" ? \"escape\" : key === \"space\" ? \" \" : key.replace(/^(?=up|down|left|right)/, \"arrow\").replace(\"_\", \"\")\n return name === event.key?.toLowerCase() && [\"meta\", \"ctrl\", \"alt\", \"shift\"].every((mod) => parts.includes(mod) === event[`${mod}Key`])\n}\n\nconst descriptors = (el) => (el.dataset.action ?? \"\").split(/\\s+/).flatMap(parseDescriptor)\n\nconst matching = (el, event, win) =>\n descriptors(el).filter((d) => d.win === win && d.type === event.type && keyMatches(d.filter, event))\n\n// Queue one firing for `el`'s root, unless that root has connected.\nfunction enqueue(event, el, descs, win, shortcut) {\n const root = el.closest(ROOT_SELECTOR)\n if (!root || state.connected.has(root)) return\n // dispatch() keeps the native flip of a checked: :keep checkbox/radio and\n // never prevents a window binding — but a shortcut (issue #357), the chord\n // the page claims (mod+s must not open the save dialog before connect), is\n // always prevented: the keepsToggle exemption is for element-bound clicks\n // only. Everything else is prevented now, as the controller would. A\n // shortcut's chord is remembered as prevented FOR a shortcut (issue #429):\n // a connected root's shortcut on the same chord still runs, as a shortcut\n // only skips a key some other handler prevented.\n //\n // `prevent: false` (issue #433) keeps the default when EVERY descriptor that\n // fired opts out: on() through its element-wide param, an on_client\n // descriptor when every record on its `on` carries `\"prevent\":false`,\n // written right after `on` (a legacy attr has no `on`, so it is prevented).\n // A record starts the attribute or follows the space mix joins records with\n // (a record writes its own spaces as \\u0020), so ` {\"on\":\"…\"` is a record's\n // start and never an op's argument.\n const keepsToggle =\n /^(checkbox|radio)$/.test(el.type) && /\"checked\":\"keep\"/.test(el.dataset.reactiveOptimisticParam)\n const ops = ` ${el.dataset.reactiveOpsParam ?? \"\"}`\n const keeps = (d) => {\n const on = ` {\"on\":${JSON.stringify(d.token.split(\"->\")[0])}`\n return d.method === \"runOps\"\n ? ops.includes(on) && ops.split(on).length === ops.split(`${on},\"prevent\":false`).length\n : keepsToggle || el.dataset.reactivePreventParam === \"false\"\n }\n if (win ? shortcut : !descs.every(keeps)) {\n if (win && !event.defaultPrevented) (state.prevented ??= new WeakSet()).add(event)\n event.preventDefault()\n }\n // Every firing is queued — a `:once` repeat included: the replay skips a\n // descriptor already consumed (reactive_controller.js #replayEarly).\n const { queue } = state\n // Bounded: a root whose controller never registers must not grow it forever.\n // `at` is the capture time — not event.timeStamp, which is when the event\n // object was CREATED (an app may build one and dispatch it much later).\n if (queue.push({ event, el, root, descs, win, at: performance.now() }) > 50) queue.shift()\n // Wake a dormant root (issue #274) — synchronously, so by the time the\n // event bubbles to an OUTER reactive root's Stimulus listener, `el` is\n // already in this root's scope. Other controllers on the root are kept.\n const { reactiveDormant, controller = \"\" } = root.dataset\n if (reactiveDormant) {\n root.dataset.controller = `${controller} ${reactiveDormant}`.trim()\n delete root.dataset.reactiveDormant\n }\n}\n\nfunction record(event) {\n for (let el = event.target; el?.closest; el = el.parentElement) {\n if (!event.bubbles && el !== event.target) break\n const descs = matching(el, event, false)\n if (descs.length) enqueue(event, el, descs, false)\n }\n}\n\n// The elements carrying an `@window` reactive descriptor (issue #303), fed by\n// the scan. One that left the page is dropped once per mutation batch (a\n// Turbo Drive visit swaps the body, so the old page's triggers do not keep it\n// alive) or when next seen by the listener.\nconst windowBound = new Set()\nexport const __windowBoundForTest = windowBound\n\nfunction recordWindow(event) {\n // Stimulus's window listener is a bubble-phase one: a non-bubbling event\n // (focus, mouseenter) from inside the page never reaches it.\n if (!event.bubbles && event.target !== event.currentTarget) return\n for (const el of windowBound) {\n if (!el.isConnected) {\n windowBound.delete(el)\n continue\n }\n // Cheapest check first: once its root connects, Stimulus hears it.\n if (state.connected.has(el.closest(ROOT_SELECTOR))) continue\n // A shortcut (issue #357): on() flags it on the element, on_client in its\n // record (read off the raw attribute: the element's records share one\n // guard here, and the replay re-checks each). The guard covers only a\n // key-filtered descriptor — a shortcut's twins always carry a filter — so\n // the element's other window bindings (a click, a bare keydown) behave\n // as they do without a shortcut. The exception is a delete/backspace\n // chord's bare keydown (below), which on that element IS the shortcut.\n const ops = el.dataset.reactiveOpsParam ?? \"\"\n const shortcut = el.dataset.reactiveShortcutParam ?? (/\"shortcut\":/.test(ops) && ops)\n const { target } = event\n const typing =\n shortcut && !/inEditable\":true/.test(shortcut) && (target.isContentEditable || /^(input|textarea|select)$/.test(target.localName))\n // Apple platforms run the meta twin of a `mod` chord, the rest the ctrl\n // one: `skip` is the other platform's.\n const mod = shortcut && (/[Mm]ac|iP/.test(navigator.userAgentData?.platform ?? navigator.platform) ? \"meta\" : \"ctrl\")\n const skip = /mod\\+/.test(shortcut) && (mod === \"meta\" ? \"ctrl\" : \"meta\")\n // A delete/backspace chord (issue #366) binds a BARE keydown (Stimulus\n // lacks the keys): the chord itself, this platform's `mod`, decides.\n const chords = (shortcut && shortcut.match(/(^|\"(keys|shortcut)\":\")[\\w+]*(delete|backspace)(?=\"|$)/g)) || []\n const bare = (d) => !d.filter && chords[0] && d.type === \"keydown\"\n // outside: is not recorded — on() flags it on the element, on_client in\n // the binding record (one window record without it is enough to queue;\n // the replay skips the outside ones).\n const descs = matching(el, event, true).filter(\n (d) =>\n (bare(d)\n ? !typing && chords.some((chord) => keyMatches(chord.slice(chord.lastIndexOf('\"') + 1).replace(\"mod\", mod), event))\n : !(d.filter && (typing || (skip && d.filter.includes(skip))))) &&\n (d.method === \"runOps\" ? /\"window\":true(?!,\"outside\")/.test(ops) : el.dataset.reactiveOutsideParam !== \"true\"),\n )\n if (descs.length) enqueue(event, el, descs, true, shortcut && descs.some((d) => d.filter || bare(d)))\n }\n}\n\n// Install the capture listeners for every reactive trigger type in `doc`, now\n// and as markup arrives. Runs on import; exported for tests and for apps that\n// render into another document.\n// addEventListener ignores a repeat of the same (type, listener, capture), so\n// every scan may simply re-add.\nexport function startEarly(doc = document) {\n const scan = (node) => {\n if (node.nodeType !== 1) return\n for (const el of [node, ...node.querySelectorAll('[data-action*=\"reactive#\"]')]) {\n for (const { type, win } of descriptors(el)) {\n if (win) windowBound.add(el)\n ;(win ? doc.defaultView : doc).addEventListener(type, win ? recordWindow : record, true)\n }\n }\n }\n // Once per batch of markup, not per added node: drop the window-bound\n // triggers that left the page (a Turbo Drive visit swaps the whole body).\n const prune = () => {\n for (const el of windowBound) if (!el.isConnected) windowBound.delete(el)\n }\n prune()\n scan(doc.documentElement)\n new MutationObserver((mutations) => {\n prune()\n for (const m of mutations) m.type === \"attributes\" ? scan(m.target) : m.addedNodes.forEach(scan)\n }).observe(doc.documentElement, { childList: true, subtree: true, attributeFilter: [\"data-action\"] })\n}\n\nif (globalThis.document) startEarly()\n"
5
+ "// phlex/reactive/early — never lose a trigger that fires before the `reactive`\n// controller connects (issue #273).\n//\n// An on()/on_client trigger is a Stimulus action: its listener only exists\n// once the controller module has loaded and connected. With a lazily loaded\n// controller (stimulus-loading's lazyLoadControllersFrom, a preload: false\n// pin, a slow network) a click or custom event in that window used to vanish:\n// no request, no error, a UI waiting forever. Import this tiny module EAGERLY\n// (`import \"phlex/reactive/early\"` in the entry point; the engine pins it with\n// preload: true) and the controller can load whenever it likes.\n//\n// How: one CAPTURE listener per trigger event type on document (the types come\n// from the `->reactive#dispatch` / `->reactive#runOps` descriptors in the page,\n// rescanned by a MutationObserver as markup arrives). When an event reaches a\n// descriptor whose root has NOT connected, it is queued and — synchronously,\n// while preventDefault still works — its native default is stopped exactly\n// where dispatch()/runOps() would stop it. connect() drains the queue\n// (reactive_controller.js #drainEarly) and replays each entry.\n//\n// The queue lives on window under a Symbol.for key, so neither module imports\n// the other and either may load first. NO Stimulus import: this module must\n// stay tiny (its gzipped size is asserted by spec/javascript/reactive_early.test.js).\n//\n// Dormant roots (issue #274): reactive_root(dormant: true) renders\n// data-reactive-dormant=\"reactive\" INSTEAD of data-controller=\"reactive\", so\n// nothing mounts (or, with a lazily loaded controller, is even fetched) until\n// one of the root's triggers fires. The first recorded trigger WAKES the root:\n// the identifier moves into data-controller, Stimulus connects it, and the\n// trigger is replayed like any other early event. A dormant root therefore\n// NEEDS this module — without it nothing ever wakes it.\n//\n// Window-bound triggers (window:, issue #303 — how a hotkey is bound) are\n// recorded too, by a capture listener on window: the keypress lands anywhere\n// on the page, so the scan keeps a registry of the elements carrying an\n// `@window` descriptor instead of walking up from the target. They are never\n// prevented (dispatch() does not prevent a window binding either) and live a\n// short TTL in the controller (a hotkey replayed seconds late is wrong).\n// A shortcut: (issue #357) is the exception: the chord is prevented, as\n// dispatch() prevents it, and the other platform's `mod` twin or a press in an\n// editable field is neither queued nor prevented.\n//\n// Limits: outside: triggers and @document bindings are not recorded (an\n// outside click before connect has nothing to close); a key filter is matched\n// against Stimulus's DEFAULT key mappings only.\n\nconst KEY = Symbol.for(\"phlex-reactive.early\")\n\n// The roots whose triggers are captured. A root counts as connected only\n// through state.connected (a WeakSet the controller fills in connect()), never\n// through markup — a Turbo snapshot clone carries data-reactive-connected over.\n// A dormant root (issue #274) is one too: it has no controller yet by design.\nconst ROOT_SELECTOR = '[data-controller~=\"reactive\"],[data-reactive-dormant~=\"reactive\"]'\n\nconst state = (globalThis[KEY] ??= { queue: [], connected: new WeakSet() })\n\n// One Stimulus descriptor token → [{ token, type, filter, win, method }] for a\n// reactive trigger, else [] (flatMap-ready). `win` marks an `@window` token;\n// `@document` never matches. Like Stimulus, a dot suffix is a key filter only\n// on a key event (`keydown.enter`); otherwise it is part of the event name\n// (`panel.opened`).\nfunction parseDescriptor(token) {\n const match = /^(?:(key\\w+)\\.([\\w+]+)|([^@>]+))(@window)?->reactive#(dispatch|runOps)(?::!?\\w+)*$/.exec(token)\n return match ? [{ token, type: match[1] ?? match[3], filter: match[2], win: !!match[4], method: match[5] }] : []\n}\n\n// Stimulus's key-filter rule against its DEFAULT mappings (esc, space, the\n// arrows, page_up/down; letters, digits, enter, tab, home, end map to\n// themselves): the key after the last \"+\" matches event.key, and the four\n// modifiers match exactly (ctrl+enter is not enter). It decides what to queue\n// and preventDefault before connect; the replay re-checks the filter against\n// the app's own key mappings (reactive_controller.js #replayEarly).\nfunction keyMatches(filter, event) {\n if (!filter) return true\n const parts = filter.split(\"+\")\n const key = parts.pop()\n const name = key === \"esc\" ? \"escape\" : key === \"space\" ? \" \" : key.replace(/^(?=up|down|left|right)/, \"arrow\").replace(\"_\", \"\")\n return name === event.key?.toLowerCase() && [\"meta\", \"ctrl\", \"alt\", \"shift\"].every((mod) => parts.includes(mod) === event[`${mod}Key`])\n}\n\nconst descriptors = (el) => (el.dataset.action ?? \"\").split(/\\s+/).flatMap(parseDescriptor)\n\nconst matching = (el, event, win) =>\n descriptors(el).filter((d) => d.win === win && d.type === event.type && keyMatches(d.filter, event))\n\n// Queue one firing for `el`'s root, unless that root has connected.\nfunction enqueue(event, el, descs, win, shortcut) {\n const root = el.closest(ROOT_SELECTOR)\n if (!root || state.connected.has(root)) return\n // dispatch() keeps the native flip of a checked: :keep checkbox/radio and\n // never prevents a window binding — but a shortcut (issue #357), the chord\n // the page claims (mod+s must not open the save dialog before connect), is\n // always prevented: the keepsToggle exemption is for element-bound clicks\n // only. Everything else is prevented now, as the controller would. A\n // shortcut's chord is remembered as prevented FOR a shortcut (issue #429):\n // a connected root's shortcut on the same chord still runs, as a shortcut\n // only skips a key some other handler prevented.\n //\n // `prevent: false` (issue #433) keeps the default when EVERY descriptor that\n // fired opts out: on() through its element-wide param, an on_client\n // descriptor when every record on its `on` carries `\"prevent\":false`,\n // written right after `on` (a legacy attr has no `on`, so it is prevented).\n // A record starts the attribute or follows the space mix joins records with\n // (a record writes its own spaces as \\u0020), so ` {\"on\":\"…\"` is a record's\n // start and never an op's argument.\n const keepsToggle =\n /^(checkbox|radio)$/.test(el.type) && /\"checked\":\"keep\"/.test(el.dataset.reactiveOptimisticParam)\n const ops = ` ${el.dataset.reactiveOpsParam ?? \"\"}`\n const keeps = (d) => {\n const on = ` {\"on\":${JSON.stringify(d.token.split(\"->\")[0])}`\n return d.method === \"runOps\"\n ? ops.includes(on) && ops.split(on).length === ops.split(`${on},\"prevent\":false`).length\n : keepsToggle || el.dataset.reactivePreventParam === \"false\"\n }\n if (win ? shortcut : !descs.every(keeps)) {\n if (win && !event.defaultPrevented) (state.prevented ??= new WeakSet()).add(event)\n event.preventDefault()\n }\n // Every firing is queued — a `:once` repeat included: the replay skips a\n // descriptor already consumed (reactive_controller.js #replayEarly).\n const { queue } = state\n // Bounded: a root whose controller never registers must not grow it forever.\n // `at` is the capture time — not event.timeStamp, which is when the event\n // object was CREATED (an app may build one and dispatch it much later).\n if (queue.push({ event, el, root, descs, win, at: performance.now() }) > 50) queue.shift()\n // Wake a dormant root (issue #274) — synchronously, so by the time the\n // event bubbles to an OUTER reactive root's Stimulus listener, `el` is\n // already in this root's scope. Other controllers on the root are kept.\n const { reactiveDormant, controller = \"\" } = root.dataset\n if (reactiveDormant) {\n root.dataset.controller = `${controller} ${reactiveDormant}`.trim()\n delete root.dataset.reactiveDormant\n }\n}\n\n// Issue #442: an on_client descriptor whose every record on its `on` is a\n// within: delegation the event's target falls outside of (no matching element\n// strictly under `el`: its parent is not inside `el`) is a miss — runOps\n// would run nothing and prevent nothing — so it is neither queued nor\n// prevented: a link in a delegated list's footer still navigates before\n// connect. A record without within:, or an attr that does not parse (a legacy\n// list), keeps the descriptor; a within: that does not parse is a miss, as\n// it is for runOps. (A row of a nested root is still queued; the replay's\n// ownership check makes it a miss.)\nconst outside = (el, target, within) => {\n try {\n return !el.contains(target.closest(within)?.parentElement ?? null)\n } catch {\n return true\n }\n}\n\nfunction delegatedMiss(el, d, target) {\n let mine\n try {\n mine = el.dataset.reactiveOpsParam.split(\" \").map(JSON.parse).filter((r) => r.on === d.token.split(\"->\")[0])\n } catch {\n return false\n }\n return mine.length > 0 && mine.every((r) => r.within != null && outside(el, target, r.within))\n}\n\nfunction record(event) {\n for (let el = event.target; el?.closest; el = el.parentElement) {\n if (!event.bubbles && el !== event.target) break\n const descs = matching(el, event, false).filter((d) => d.method !== \"runOps\" || !delegatedMiss(el, d, event.target))\n if (descs.length) enqueue(event, el, descs, false)\n }\n}\n\n// The elements carrying an `@window` reactive descriptor (issue #303), fed by\n// the scan. One that left the page is dropped once per mutation batch (a\n// Turbo Drive visit swaps the body, so the old page's triggers do not keep it\n// alive) or when next seen by the listener.\nconst windowBound = new Set()\nexport const __windowBoundForTest = windowBound\n\nfunction recordWindow(event) {\n // Stimulus's window listener is a bubble-phase one: a non-bubbling event\n // (focus, mouseenter) from inside the page never reaches it.\n if (!event.bubbles && event.target !== event.currentTarget) return\n for (const el of windowBound) {\n if (!el.isConnected) {\n windowBound.delete(el)\n continue\n }\n // Cheapest check first: once its root connects, Stimulus hears it.\n if (state.connected.has(el.closest(ROOT_SELECTOR))) continue\n // A shortcut (issue #357): on() flags it on the element, on_client in its\n // record (read off the raw attribute: the element's records share one\n // guard here, and the replay re-checks each). The guard covers only a\n // key-filtered descriptor — a shortcut's twins always carry a filter — so\n // the element's other window bindings (a click, a bare keydown) behave\n // as they do without a shortcut. The exception is a delete/backspace\n // chord's bare keydown (below), which on that element IS the shortcut.\n const ops = el.dataset.reactiveOpsParam ?? \"\"\n const shortcut = el.dataset.reactiveShortcutParam ?? (/\"shortcut\":/.test(ops) && ops)\n const { target } = event\n const typing =\n shortcut && !/inEditable\":true/.test(shortcut) && (target.isContentEditable || /^(input|textarea|select)$/.test(target.localName))\n // Apple platforms run the meta twin of a `mod` chord, the rest the ctrl\n // one: `skip` is the other platform's.\n const mod = shortcut && (/[Mm]ac|iP/.test(navigator.userAgentData?.platform ?? navigator.platform) ? \"meta\" : \"ctrl\")\n const skip = /mod\\+/.test(shortcut) && (mod === \"meta\" ? \"ctrl\" : \"meta\")\n // A delete/backspace chord (issue #366) binds a BARE keydown (Stimulus\n // lacks the keys): the chord itself, this platform's `mod`, decides.\n const chords = (shortcut && shortcut.match(/(^|\"(keys|shortcut)\":\")[\\w+]*(delete|backspace)(?=\"|$)/g)) || []\n const bare = (d) => !d.filter && chords[0] && d.type === \"keydown\"\n // outside: is not recorded — on() flags it on the element, on_client in\n // the binding record (one window record without it is enough to queue;\n // the replay skips the outside ones).\n const descs = matching(el, event, true).filter(\n (d) =>\n (bare(d)\n ? !typing && chords.some((chord) => keyMatches(chord.slice(chord.lastIndexOf('\"') + 1).replace(\"mod\", mod), event))\n : !(d.filter && (typing || (skip && d.filter.includes(skip))))) &&\n (d.method === \"runOps\" ? /\"window\":true(?!,\"outside\")/.test(ops) : el.dataset.reactiveOutsideParam !== \"true\"),\n )\n if (descs.length) enqueue(event, el, descs, true, shortcut && descs.some((d) => d.filter || bare(d)))\n }\n}\n\n// Install the capture listeners for every reactive trigger type in `doc`, now\n// and as markup arrives. Runs on import; exported for tests and for apps that\n// render into another document.\n// addEventListener ignores a repeat of the same (type, listener, capture), so\n// every scan may simply re-add.\nexport function startEarly(doc = document) {\n const scan = (node) => {\n if (node.nodeType !== 1) return\n for (const el of [node, ...node.querySelectorAll('[data-action*=\"reactive#\"]')]) {\n for (const { type, win } of descriptors(el)) {\n if (win) windowBound.add(el)\n ;(win ? doc.defaultView : doc).addEventListener(type, win ? recordWindow : record, true)\n }\n }\n }\n // Once per batch of markup, not per added node: drop the window-bound\n // triggers that left the page (a Turbo Drive visit swaps the whole body).\n const prune = () => {\n for (const el of windowBound) if (!el.isConnected) windowBound.delete(el)\n }\n prune()\n scan(doc.documentElement)\n new MutationObserver((mutations) => {\n prune()\n for (const m of mutations) m.type === \"attributes\" ? scan(m.target) : m.addedNodes.forEach(scan)\n }).observe(doc.documentElement, { childList: true, subtree: true, attributeFilter: [\"data-action\"] })\n}\n\nif (globalThis.document) startEarly()\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "E569865D485AB4CD64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "B1FCA9A2D1DB67F064756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -129,6 +129,14 @@ function countMatches(node, selector) {
129
129
  }
130
130
  }
131
131
 
132
+ function matchesSelf(node, selector) {
133
+ try {
134
+ return node?.matches?.(selector) === true
135
+ } catch {
136
+ return false
137
+ }
138
+ }
139
+
132
140
  function emitZeroTargetWarn(label, to, scope, hint) {
133
141
  if (zeroTargetAlreadyWarned(`${label}|${to}|${scope}`)) return
134
142
  console.warn(`[phlex-reactive] ${label} matched zero targets for selector "${to}" (${scope})${hint}`)
@@ -141,8 +149,8 @@ function emitZeroTargetWarn(label, to, scope, hint) {
141
149
  export function diagnoseStream(name, args, root) {
142
150
  const to = args.to
143
151
  if (typeof to !== "string" || to === "" || to === "@root") return
144
- let hint = ""
145
- if (root && !args.global) {
152
+ let hint = to === "@trigger" ? " — a server-pushed op has no trigger element" : ""
153
+ if (!hint && root && !args.global) {
146
154
  const n = countMatches(globalThis.document, to)
147
155
  if (n > 0) hint = ` — it matches ${n} element(s) outside the stream's target root; use global: true`
148
156
  }
@@ -166,9 +174,11 @@ export function diagnose(controller, label, args) {
166
174
  const root = controller.element
167
175
  const to = args.to
168
176
  if (typeof to !== "string" || to === "" || to === "@root") return
169
- let hint = ""
170
- if (!args.global) {
171
- if (root?.matches?.(to)) {
177
+ // Issue #442: "@trigger" is no selector — probing it would throw. It matched
178
+ // nothing because this run had no trigger element.
179
+ let hint = to === "@trigger" ? " — this run has no trigger element" : ""
180
+ if (!hint && !args.global) {
181
+ if (matchesSelf(root, to)) {
172
182
  hint = " — the selector matches this component's own root, which root-scoped resolution never includes; use to: :root"
173
183
  } else if (countMatches(root, to) > 0) {
174
184
  hint = " — it matches only inside a nested reactive root (excluded by ownership scoping); use global: true"
@@ -1,4 +1,4 @@
1
- var p="phlex-reactive:latency",c=!1;function f(e){if(typeof sessionStorage>"u")return;sessionStorage.setItem("phlex-reactive:latency",String(e))}function h(){if(typeof sessionStorage>"u")return;sessionStorage.removeItem("phlex-reactive:latency"),c=!1}function y(){if(typeof window<"u")window.PhlexReactive={enableLatencySim:f,disableLatencySim:h}}function x(){c=!1}function b(){if(typeof sessionStorage>"u")return Promise.resolve();let e=Number(sessionStorage.getItem("phlex-reactive:latency"));if(!Number.isFinite(e)||e<=0)return Promise.resolve();if(!c)c=!0,console.warn(`[phlex-reactive] latency simulator ACTIVE — every action is delayed by ${e}ms. Call PhlexReactive.disableLatencySim() (or clear sessionStorage) to turn it off.`);return new Promise((t)=>setTimeout(t,e))}var u=new WeakMap;function l(e){let t=globalThis.document;if(!t)return!0;let o=u.get(t);if(!o)o=new Set,u.set(t,o);if(o.has(e))return!0;return o.add(e),!1}function a(e,t){try{return e?.querySelectorAll?.(t)?.length??0}catch{return 0}}function d(e,t,o,n){if(l(`${e}|${t}|${o}`))return;console.warn(`[phlex-reactive] ${e} matched zero targets for selector "${t}" (${o})${n}`)}function w(e,t,o){let n=t.to;if(typeof n!=="string"||n===""||n==="@root")return;let r="";if(o&&!t.global){let s=a(globalThis.document,n);if(s>0)r=` — it matches ${s} element(s) outside the stream's target root; use global: true`}d(`client op "${e}"`,n,o?`scoped to #${o.id||"?"}`:"document-scoped",r)}function S(e){if(!l(`missing-root|#${e}`))console.warn(`[phlex-reactive] reactive:js stream target root #${e} is not in the DOM — its ops were dropped`)}function v(e,t,o){let n=e.element,r=o.to;if(typeof r!=="string"||r===""||r==="@root")return;let s="";if(!o.global)if(n?.matches?.(r))s=" — the selector matches this component's own root, which root-scoped resolution never includes; use to: :root";else if(a(n,r)>0)s=" — it matches only inside a nested reactive root (excluded by ownership scoping); use global: true";else{let i=a(globalThis.document,r);if(i>0)s=` — it matches ${i} element(s) outside this scope; use global: true`}d(t,r,`scoped to #${n?.id||"?"}`,s)}function T(e,t){let o=e.element?.id||"?";if(l(`no-binding|${t.type}|${o}`))return;console.warn(`[phlex-reactive] runOps on #${o} found no on_client binding matching a "${t.type}" event — nothing ran`)}function g(e){if(!e)return[];let t=[],o=/<turbo-stream\b([^>]*)>/g,n;while((n=o.exec(e))!==null){let r=n[1],s=r.match(/\baction="([^"]*)"/)?.[1]??"?",i=r.match(/\btarget="([^"]*)"/)?.[1];t.push(i?`${s} → #${i}`:s)}return t}function E(e,t){let{action:o,status:n,ms:r}=t,s=e.element?.id,m=`reactive ${s?`#${s} `:""}${o} → ${n??"—"} (${Math.round(r)}ms)`;if(console.groupCollapsed(m),console.log(`params: [${t.paramNames.join(", ")}] + collected: [${t.fieldNames.join(", ")}]`),console.log(`encoding: ${t.encoding}`),t.streams.length)console.log(`streams: ${t.streams.join(" ")}`);console.log(`token: ${t.tokenRefreshed?"refreshed ✓":"unchanged"}`),console.groupEnd()}function L(e,t,o){e.streams=g(t),e.tokenRefreshed=o!=null}export{p as LATENCY_KEY,y as attach,b as delay,v as diagnose,w as diagnoseStream,h as disableLatencySim,f as enableLatencySim,S as missingRoot,T as noBinding,L as recordBody,x as resetLatencySim,E as trace};
1
+ var y="phlex-reactive:latency",a=!1;function f(e){if(typeof sessionStorage>"u")return;sessionStorage.setItem("phlex-reactive:latency",String(e))}function g(){if(typeof sessionStorage>"u")return;sessionStorage.removeItem("phlex-reactive:latency"),a=!1}function x(){if(typeof window<"u")window.PhlexReactive={enableLatencySim:f,disableLatencySim:g}}function b(){a=!1}function w(){if(typeof sessionStorage>"u")return Promise.resolve();let e=Number(sessionStorage.getItem("phlex-reactive:latency"));if(!Number.isFinite(e)||e<=0)return Promise.resolve();if(!a)a=!0,console.warn(`[phlex-reactive] latency simulator ACTIVE — every action is delayed by ${e}ms. Call PhlexReactive.disableLatencySim() (or clear sessionStorage) to turn it off.`);return new Promise((t)=>setTimeout(t,e))}var u=new WeakMap;function l(e){let t=globalThis.document;if(!t)return!0;let o=u.get(t);if(!o)o=new Set,u.set(t,o);if(o.has(e))return!0;return o.add(e),!1}function c(e,t){try{return e?.querySelectorAll?.(t)?.length??0}catch{return 0}}function h(e,t){try{return e?.matches?.(t)===!0}catch{return!1}}function d(e,t,o,n){if(l(`${e}|${t}|${o}`))return;console.warn(`[phlex-reactive] ${e} matched zero targets for selector "${t}" (${o})${n}`)}function S(e,t,o){let n=t.to;if(typeof n!=="string"||n===""||n==="@root")return;let r=n==="@trigger"?" — a server-pushed op has no trigger element":"";if(!r&&o&&!t.global){let s=c(globalThis.document,n);if(s>0)r=` — it matches ${s} element(s) outside the stream's target root; use global: true`}d(`client op "${e}"`,n,o?`scoped to #${o.id||"?"}`:"document-scoped",r)}function v(e){if(!l(`missing-root|#${e}`))console.warn(`[phlex-reactive] reactive:js stream target root #${e} is not in the DOM — its ops were dropped`)}function T(e,t,o){let n=e.element,r=o.to;if(typeof r!=="string"||r===""||r==="@root")return;let s=r==="@trigger"?" — this run has no trigger element":"";if(!s&&!o.global)if(h(n,r))s=" — the selector matches this component's own root, which root-scoped resolution never includes; use to: :root";else if(c(n,r)>0)s=" — it matches only inside a nested reactive root (excluded by ownership scoping); use global: true";else{let i=c(globalThis.document,r);if(i>0)s=` — it matches ${i} element(s) outside this scope; use global: true`}d(t,r,`scoped to #${n?.id||"?"}`,s)}function E(e,t){let o=e.element?.id||"?";if(l(`no-binding|${t.type}|${o}`))return;console.warn(`[phlex-reactive] runOps on #${o} found no on_client binding matching a "${t.type}" event — nothing ran`)}function p(e){if(!e)return[];let t=[],o=/<turbo-stream\b([^>]*)>/g,n;while((n=o.exec(e))!==null){let r=n[1],s=r.match(/\baction="([^"]*)"/)?.[1]??"?",i=r.match(/\btarget="([^"]*)"/)?.[1];t.push(i?`${s} → #${i}`:s)}return t}function L(e,t){let{action:o,status:n,ms:r}=t,s=e.element?.id,m=`reactive ${s?`#${s} `:""}${o} → ${n??"—"} (${Math.round(r)}ms)`;if(console.groupCollapsed(m),console.log(`params: [${t.paramNames.join(", ")}] + collected: [${t.fieldNames.join(", ")}]`),console.log(`encoding: ${t.encoding}`),t.streams.length)console.log(`streams: ${t.streams.join(" ")}`);console.log(`token: ${t.tokenRefreshed?"refreshed ✓":"unchanged"}`),console.groupEnd()}function N(e,t,o){e.streams=p(t),e.tokenRefreshed=o!=null}export{y as LATENCY_KEY,x as attach,w as delay,T as diagnose,S as diagnoseStream,g as disableLatencySim,f as enableLatencySim,v as missingRoot,E as noBinding,N as recordBody,b as resetLatencySim,L as trace};
2
2
 
3
- //# debugId=FE47EA9129EA3AE764756E2164756E21
3
+ //# debugId=B6C25F611EB1EE0B64756E2164756E21
4
4
  //# sourceMappingURL=devtools.min.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["features/devtools.js"],
4
4
  "sourcesContent": [
5
- "// phlex/reactive/features/devtools — development aids: the latency\n// simulator (issue #102), the zero-target diagnostics (issue #237) and the\n// client debug trace (issue #108). One of the feature modules the opt-in\n// client imports on demand (issue #275); in the default client it is part of\n// the one file.\n//\n// When the opt-in client imports it: at registration on a page that carries\n// <meta name=\"phlex-reactive-env\" content=\"development\"> or has a latency\n// delay stored for the tab (effectively eager, in development); at connect\n// for a root stamped data-reactive-debug=\"true\"; and otherwise the first\n// time a verbose root has something to warn about, after which the warning\n// is a tick late. The runtime keeps the gates (the debug and verbose attribute\n// reads) and asks for the module only past them, so a production page with\n// neither never fetches it.\n//\n// This module never imports the runtime: it reaches a controller through the\n// `core` handle.\n\n// Latency simulator dev aid (issue #102). On localhost the click→morph round\n// trip is ~5ms, so the pending/loading/optimistic affordances (aria-busy,\n// disable_with, busy_on, optimistic hints) flash by too fast to see while\n// developing or demoing them — the reason LiveView ships enableLatencySim(ms).\n//\n// enableLatencySim(ms) persists the delay to sessionStorage (session-scoped, so\n// it clears when the tab closes — never a config you forget you left on); the\n// controller awaits delay() right before the fetch, stretching the already-set\n// busy window to something visible. disableLatencySim() clears it. NAMED\n// exports (the setConfirmResolver precedent; phlex/reactive/reactive_controller\n// re-exports them) — but importmap module exports are unreachable from the\n// browser console, so attach() ALSO puts them on window.PhlexReactive, and\n// ONLY when the app opts in with\n// <meta name=\"phlex-reactive-env\" content=\"development\">.\nexport const LATENCY_KEY = \"phlex-reactive:latency\"\n\n// One-time \"sim active\" banner guard: delay() warns ONCE while the sim is on,\n// not once per request.\nlet latencyBannerShown = false\n\nexport function enableLatencySim(ms) {\n if (typeof sessionStorage === \"undefined\") return\n sessionStorage.setItem(LATENCY_KEY, String(ms))\n}\n\nexport function disableLatencySim() {\n if (typeof sessionStorage === \"undefined\") return\n sessionStorage.removeItem(LATENCY_KEY)\n // Re-arm the one-time \"sim active\" banner: turning the sim OFF is the lifecycle\n // boundary, so a later enableLatencySim() in the same session re-announces that\n // the sim is on (otherwise the guard would stay set across an off→on cycle and\n // swallow the banner).\n latencyBannerShown = false\n}\n\n// The console handle. importmap module exports aren't reachable from the\n// DevTools console, so the two functions also go on a window global — which\n// the core asks for ONLY when the app authored\n// <meta name=\"phlex-reactive-env\" content=\"development\">. There is NO\n// engine-emitted meta (the engine can't inject into the host layout); the\n// install generator ships the snippet commented. Without the meta: no global\n// handle at all — zero production surface.\nexport function attach() {\n if (typeof window !== \"undefined\") window.PhlexReactive = { enableLatencySim, disableLatencySim }\n}\n\n// Forget the one-time active-sim banner (tests).\nexport function resetLatencySim() {\n latencyBannerShown = false\n}\n\n// If enableLatencySim(ms) stored a delay in sessionStorage, resolve after it —\n// the controller awaits this before the fetch so the busy window (already open\n// since enqueue) is actually visible on localhost. Reads the key LIVE per\n// request — like the CSRF token — so toggling the sim mid-session takes effect\n// on the very next action without a reload. A missing sessionStorage, an absent\n// key, or a non-positive/NaN value resolves immediately (no timer, no delay) —\n// the whole feature is inert for any app that never opts in.\nexport function delay() {\n if (typeof sessionStorage === \"undefined\") return Promise.resolve()\n const ms = Number(sessionStorage.getItem(LATENCY_KEY))\n if (!Number.isFinite(ms) || ms <= 0) return Promise.resolve()\n if (!latencyBannerShown) {\n latencyBannerShown = true\n console.warn(\n `[phlex-reactive] latency simulator ACTIVE — every action is delayed by ${ms}ms. ` +\n \"Call PhlexReactive.disableLatencySim() (or clear sessionStorage) to turn it off.\",\n )\n }\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n// --- The zero-target diagnostics (issue #237) ---------------------------------\n// The runtime calls these only past the verbose gate.\n// --- zero-target diagnostics (issue #237) -----------------------------------\n// An op resolving ZERO targets is indistinguishable from a working no-op, and\n// the documented scoping traps (nested-root ownership filter, root-self\n// selector, stream default scope) all present exactly that way. Under the\n// verbose gate (data-reactive-verbose, stamped when Phlex::Reactive\n// .verbose_errors is on — dev/test by default — or the debug attr) warn ONCE\n// per unique (label, selector, scope) with a targeted hint when the element\n// EXISTS but sits outside the op's scope. Everything below runs only after a\n// zero-match with the gate on; production (no attr) pays one boolean per empty\n// resolution and never probes the DOM.\n//\n// Dedupe is keyed per document (page lifetime): a WeakMap entry per document\n// means a fresh page — or a fresh unit-harness stub — starts clean, and the\n// per-keystroke reducer ($ops) path can never flood the console.\nconst zeroTargetWarnSets = new WeakMap()\n\nfunction zeroTargetAlreadyWarned(key) {\n const doc = globalThis.document\n if (!doc) return true\n let seen = zeroTargetWarnSets.get(doc)\n if (!seen) {\n seen = new Set()\n zeroTargetWarnSets.set(doc, seen)\n }\n if (seen.has(key)) return true\n seen.add(key)\n return false\n}\n\n// Guarded probes: unit harnesses stub partial documents/roots, and an exotic\n// selector could throw — a diagnostic must never break the op pipeline.\nfunction countMatches(node, selector) {\n try {\n return node?.querySelectorAll?.(selector)?.length ?? 0\n } catch {\n return 0\n }\n}\n\nfunction emitZeroTargetWarn(label, to, scope, hint) {\n if (zeroTargetAlreadyWarned(`${label}|${to}|${scope}`)) return\n console.warn(`[phlex-reactive] ${label} matched zero targets for selector \"${to}\" (${scope})${hint}`)\n}\n\n// The stream-path diagnoser (reactive:js): no root, or the stream's target\n// root. No ownership filter exists here, so the only trap is the target-root\n// scope: the selector matches document-wide but the op was scoped to the\n// stream's target root.\nexport function diagnoseStream(name, args, root) {\n const to = args.to\n if (typeof to !== \"string\" || to === \"\" || to === \"@root\") return\n let hint = \"\"\n if (root && !args.global) {\n const n = countMatches(globalThis.document, to)\n if (n > 0) hint = ` — it matches ${n} element(s) outside the stream's target root; use global: true`\n }\n emitZeroTargetWarn(`client op \"${name}\"`, to, root ? `scoped to #${root.id || \"?\"}` : \"document-scoped\", hint)\n}\n\n// The runtime's call-ins below take the controller; each reads its root.\n\nexport function missingRoot(targetId) {\n if (!zeroTargetAlreadyWarned(`missing-root|#${targetId}`)) {\n console.warn(`[phlex-reactive] reactive:js stream target root #${targetId} is not in the DOM — its ops were dropped`)\n }\n}\n\n// Issue #237: called when a selector-form target resolved to ZERO elements on\n// this root. Gated + deduped (module helpers); builds the trap-specific hint:\n// the root-self selector (root-scoped resolution never includes the root),\n// the nested-reactive-root ownership filter, or plain out-of-scope. All DOM\n// probes run only here — after a zero-match with the gate on.\nexport function diagnose(controller, label, args) {\n const root = controller.element\n const to = args.to\n if (typeof to !== \"string\" || to === \"\" || to === \"@root\") return\n let hint = \"\"\n if (!args.global) {\n if (root?.matches?.(to)) {\n hint = \" — the selector matches this component's own root, which root-scoped resolution never includes; use to: :root\"\n } else if (countMatches(root, to) > 0) {\n hint = \" — it matches only inside a nested reactive root (excluded by ownership scoping); use global: true\"\n } else {\n const n = countMatches(globalThis.document, to)\n if (n > 0) hint = ` — it matches ${n} element(s) outside this scope; use global: true`\n }\n }\n emitZeroTargetWarn(label, to, `scoped to #${root?.id || \"?\"}`, hint)\n}\n\n// Issue #271: runOps fired but no record matched the event — a hand-edited\n// attr or a descriptor the matcher doesn't know. Verbose gate only, deduped.\nexport function noBinding(controller, event) {\n const id = controller.element?.id || \"?\"\n if (zeroTargetAlreadyWarned(`no-binding|${event.type}|${id}`)) return\n console.warn(`[phlex-reactive] runOps on #${id} found no on_client binding matching a \"${event.type}\" event — nothing ran`)\n}\n\n// --- The debug trace (issue #108) -----------------------------------------------\n\n// Parse a turbo-stream response's action + target pairs for the debug trace,\n// from the body text #perform ALREADY read (never a re-fetch). NAMES only — the\n// <template> contents (rendered HTML, the fresh token) are deliberately not\n// touched. A non-turbo-stream / empty body yields [] (nothing to report).\nfunction debugStreams(body) {\n if (!body) return []\n const streams = []\n const re = /<turbo-stream\\b([^>]*)>/g\n let match\n while ((match = re.exec(body)) !== null) {\n const attrs = match[1]\n const action = attrs.match(/\\baction=\"([^\"]*)\"/)?.[1] ?? \"?\"\n const target = attrs.match(/\\btarget=\"([^\"]*)\"/)?.[1]\n streams.push(target ? `${action} → #${target}` : action)\n }\n return streams\n}\n\n// console.group ONE dispatch (issue #108). Carries NAMES + outcomes ONLY — the\n// signed token VALUE and every field/param VALUE are deliberately absent (they\n// may be sensitive; the whole point is observability without leaking data). The\n// caller passes the info it already holds so nothing is recomputed or re-fetched:\n// { action, paramNames, fieldNames, encoding, status, streams, tokenRefreshed, ms }\n// `console.groupCollapsed` keeps the console tidy (one collapsed line per action).\nexport function trace(controller, info) {\n const { action, status, ms } = info\n // The client can't name the component CLASS (it's inside the signed, opaque\n // token — never decoded here), but the root's id is the stable client-side\n // handle (e.g. #todo_42), so the header reads `reactive #todo_42 rename → …`.\n const id = controller.element?.id\n const who = id ? `#${id} ` : \"\"\n const header = `reactive ${who}${action} → ${status ?? \"—\"} (${Math.round(ms)}ms)`\n /* eslint-disable no-console */\n console.groupCollapsed(header)\n console.log(`params: [${info.paramNames.join(\", \")}] + collected: [${info.fieldNames.join(\", \")}]`)\n console.log(`encoding: ${info.encoding}`)\n if (info.streams.length) console.log(`streams: ${info.streams.join(\" \")}`)\n console.log(`token: ${info.tokenRefreshed ? \"refreshed ✓\" : \"unchanged\"}`)\n console.groupEnd()\n /* eslint-enable no-console */\n}\n\n// Debug (issue #108): fold the response body #perform already read into the\n// trace — the stream action/target pairs and whether a token refresh arrived\n// (a boolean; the token VALUE is intentionally not stored). Shared by the\n// success and the non-OK-turbo-stream branches so both log the same shape.\nexport function recordBody(debug, body, freshToken) {\n debug.streams = debugStreams(body)\n debug.tokenRefreshed = freshToken != null\n}\n"
5
+ "// phlex/reactive/features/devtools — development aids: the latency\n// simulator (issue #102), the zero-target diagnostics (issue #237) and the\n// client debug trace (issue #108). One of the feature modules the opt-in\n// client imports on demand (issue #275); in the default client it is part of\n// the one file.\n//\n// When the opt-in client imports it: at registration on a page that carries\n// <meta name=\"phlex-reactive-env\" content=\"development\"> or has a latency\n// delay stored for the tab (effectively eager, in development); at connect\n// for a root stamped data-reactive-debug=\"true\"; and otherwise the first\n// time a verbose root has something to warn about, after which the warning\n// is a tick late. The runtime keeps the gates (the debug and verbose attribute\n// reads) and asks for the module only past them, so a production page with\n// neither never fetches it.\n//\n// This module never imports the runtime: it reaches a controller through the\n// `core` handle.\n\n// Latency simulator dev aid (issue #102). On localhost the click→morph round\n// trip is ~5ms, so the pending/loading/optimistic affordances (aria-busy,\n// disable_with, busy_on, optimistic hints) flash by too fast to see while\n// developing or demoing them — the reason LiveView ships enableLatencySim(ms).\n//\n// enableLatencySim(ms) persists the delay to sessionStorage (session-scoped, so\n// it clears when the tab closes — never a config you forget you left on); the\n// controller awaits delay() right before the fetch, stretching the already-set\n// busy window to something visible. disableLatencySim() clears it. NAMED\n// exports (the setConfirmResolver precedent; phlex/reactive/reactive_controller\n// re-exports them) — but importmap module exports are unreachable from the\n// browser console, so attach() ALSO puts them on window.PhlexReactive, and\n// ONLY when the app opts in with\n// <meta name=\"phlex-reactive-env\" content=\"development\">.\nexport const LATENCY_KEY = \"phlex-reactive:latency\"\n\n// One-time \"sim active\" banner guard: delay() warns ONCE while the sim is on,\n// not once per request.\nlet latencyBannerShown = false\n\nexport function enableLatencySim(ms) {\n if (typeof sessionStorage === \"undefined\") return\n sessionStorage.setItem(LATENCY_KEY, String(ms))\n}\n\nexport function disableLatencySim() {\n if (typeof sessionStorage === \"undefined\") return\n sessionStorage.removeItem(LATENCY_KEY)\n // Re-arm the one-time \"sim active\" banner: turning the sim OFF is the lifecycle\n // boundary, so a later enableLatencySim() in the same session re-announces that\n // the sim is on (otherwise the guard would stay set across an off→on cycle and\n // swallow the banner).\n latencyBannerShown = false\n}\n\n// The console handle. importmap module exports aren't reachable from the\n// DevTools console, so the two functions also go on a window global — which\n// the core asks for ONLY when the app authored\n// <meta name=\"phlex-reactive-env\" content=\"development\">. There is NO\n// engine-emitted meta (the engine can't inject into the host layout); the\n// install generator ships the snippet commented. Without the meta: no global\n// handle at all — zero production surface.\nexport function attach() {\n if (typeof window !== \"undefined\") window.PhlexReactive = { enableLatencySim, disableLatencySim }\n}\n\n// Forget the one-time active-sim banner (tests).\nexport function resetLatencySim() {\n latencyBannerShown = false\n}\n\n// If enableLatencySim(ms) stored a delay in sessionStorage, resolve after it —\n// the controller awaits this before the fetch so the busy window (already open\n// since enqueue) is actually visible on localhost. Reads the key LIVE per\n// request — like the CSRF token — so toggling the sim mid-session takes effect\n// on the very next action without a reload. A missing sessionStorage, an absent\n// key, or a non-positive/NaN value resolves immediately (no timer, no delay) —\n// the whole feature is inert for any app that never opts in.\nexport function delay() {\n if (typeof sessionStorage === \"undefined\") return Promise.resolve()\n const ms = Number(sessionStorage.getItem(LATENCY_KEY))\n if (!Number.isFinite(ms) || ms <= 0) return Promise.resolve()\n if (!latencyBannerShown) {\n latencyBannerShown = true\n console.warn(\n `[phlex-reactive] latency simulator ACTIVE — every action is delayed by ${ms}ms. ` +\n \"Call PhlexReactive.disableLatencySim() (or clear sessionStorage) to turn it off.\",\n )\n }\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n\n// --- The zero-target diagnostics (issue #237) ---------------------------------\n// The runtime calls these only past the verbose gate.\n// --- zero-target diagnostics (issue #237) -----------------------------------\n// An op resolving ZERO targets is indistinguishable from a working no-op, and\n// the documented scoping traps (nested-root ownership filter, root-self\n// selector, stream default scope) all present exactly that way. Under the\n// verbose gate (data-reactive-verbose, stamped when Phlex::Reactive\n// .verbose_errors is on — dev/test by default — or the debug attr) warn ONCE\n// per unique (label, selector, scope) with a targeted hint when the element\n// EXISTS but sits outside the op's scope. Everything below runs only after a\n// zero-match with the gate on; production (no attr) pays one boolean per empty\n// resolution and never probes the DOM.\n//\n// Dedupe is keyed per document (page lifetime): a WeakMap entry per document\n// means a fresh page — or a fresh unit-harness stub — starts clean, and the\n// per-keystroke reducer ($ops) path can never flood the console.\nconst zeroTargetWarnSets = new WeakMap()\n\nfunction zeroTargetAlreadyWarned(key) {\n const doc = globalThis.document\n if (!doc) return true\n let seen = zeroTargetWarnSets.get(doc)\n if (!seen) {\n seen = new Set()\n zeroTargetWarnSets.set(doc, seen)\n }\n if (seen.has(key)) return true\n seen.add(key)\n return false\n}\n\n// Guarded probes: unit harnesses stub partial documents/roots, and an exotic\n// selector could throw — a diagnostic must never break the op pipeline.\nfunction countMatches(node, selector) {\n try {\n return node?.querySelectorAll?.(selector)?.length ?? 0\n } catch {\n return 0\n }\n}\n\nfunction matchesSelf(node, selector) {\n try {\n return node?.matches?.(selector) === true\n } catch {\n return false\n }\n}\n\nfunction emitZeroTargetWarn(label, to, scope, hint) {\n if (zeroTargetAlreadyWarned(`${label}|${to}|${scope}`)) return\n console.warn(`[phlex-reactive] ${label} matched zero targets for selector \"${to}\" (${scope})${hint}`)\n}\n\n// The stream-path diagnoser (reactive:js): no root, or the stream's target\n// root. No ownership filter exists here, so the only trap is the target-root\n// scope: the selector matches document-wide but the op was scoped to the\n// stream's target root.\nexport function diagnoseStream(name, args, root) {\n const to = args.to\n if (typeof to !== \"string\" || to === \"\" || to === \"@root\") return\n let hint = to === \"@trigger\" ? \" — a server-pushed op has no trigger element\" : \"\"\n if (!hint && root && !args.global) {\n const n = countMatches(globalThis.document, to)\n if (n > 0) hint = ` — it matches ${n} element(s) outside the stream's target root; use global: true`\n }\n emitZeroTargetWarn(`client op \"${name}\"`, to, root ? `scoped to #${root.id || \"?\"}` : \"document-scoped\", hint)\n}\n\n// The runtime's call-ins below take the controller; each reads its root.\n\nexport function missingRoot(targetId) {\n if (!zeroTargetAlreadyWarned(`missing-root|#${targetId}`)) {\n console.warn(`[phlex-reactive] reactive:js stream target root #${targetId} is not in the DOM — its ops were dropped`)\n }\n}\n\n// Issue #237: called when a selector-form target resolved to ZERO elements on\n// this root. Gated + deduped (module helpers); builds the trap-specific hint:\n// the root-self selector (root-scoped resolution never includes the root),\n// the nested-reactive-root ownership filter, or plain out-of-scope. All DOM\n// probes run only here — after a zero-match with the gate on.\nexport function diagnose(controller, label, args) {\n const root = controller.element\n const to = args.to\n if (typeof to !== \"string\" || to === \"\" || to === \"@root\") return\n // Issue #442: \"@trigger\" is no selector — probing it would throw. It matched\n // nothing because this run had no trigger element.\n let hint = to === \"@trigger\" ? \" — this run has no trigger element\" : \"\"\n if (!hint && !args.global) {\n if (matchesSelf(root, to)) {\n hint = \" — the selector matches this component's own root, which root-scoped resolution never includes; use to: :root\"\n } else if (countMatches(root, to) > 0) {\n hint = \" — it matches only inside a nested reactive root (excluded by ownership scoping); use global: true\"\n } else {\n const n = countMatches(globalThis.document, to)\n if (n > 0) hint = ` — it matches ${n} element(s) outside this scope; use global: true`\n }\n }\n emitZeroTargetWarn(label, to, `scoped to #${root?.id || \"?\"}`, hint)\n}\n\n// Issue #271: runOps fired but no record matched the event — a hand-edited\n// attr or a descriptor the matcher doesn't know. Verbose gate only, deduped.\nexport function noBinding(controller, event) {\n const id = controller.element?.id || \"?\"\n if (zeroTargetAlreadyWarned(`no-binding|${event.type}|${id}`)) return\n console.warn(`[phlex-reactive] runOps on #${id} found no on_client binding matching a \"${event.type}\" event — nothing ran`)\n}\n\n// --- The debug trace (issue #108) -----------------------------------------------\n\n// Parse a turbo-stream response's action + target pairs for the debug trace,\n// from the body text #perform ALREADY read (never a re-fetch). NAMES only — the\n// <template> contents (rendered HTML, the fresh token) are deliberately not\n// touched. A non-turbo-stream / empty body yields [] (nothing to report).\nfunction debugStreams(body) {\n if (!body) return []\n const streams = []\n const re = /<turbo-stream\\b([^>]*)>/g\n let match\n while ((match = re.exec(body)) !== null) {\n const attrs = match[1]\n const action = attrs.match(/\\baction=\"([^\"]*)\"/)?.[1] ?? \"?\"\n const target = attrs.match(/\\btarget=\"([^\"]*)\"/)?.[1]\n streams.push(target ? `${action} → #${target}` : action)\n }\n return streams\n}\n\n// console.group ONE dispatch (issue #108). Carries NAMES + outcomes ONLY — the\n// signed token VALUE and every field/param VALUE are deliberately absent (they\n// may be sensitive; the whole point is observability without leaking data). The\n// caller passes the info it already holds so nothing is recomputed or re-fetched:\n// { action, paramNames, fieldNames, encoding, status, streams, tokenRefreshed, ms }\n// `console.groupCollapsed` keeps the console tidy (one collapsed line per action).\nexport function trace(controller, info) {\n const { action, status, ms } = info\n // The client can't name the component CLASS (it's inside the signed, opaque\n // token — never decoded here), but the root's id is the stable client-side\n // handle (e.g. #todo_42), so the header reads `reactive #todo_42 rename → …`.\n const id = controller.element?.id\n const who = id ? `#${id} ` : \"\"\n const header = `reactive ${who}${action} → ${status ?? \"—\"} (${Math.round(ms)}ms)`\n /* eslint-disable no-console */\n console.groupCollapsed(header)\n console.log(`params: [${info.paramNames.join(\", \")}] + collected: [${info.fieldNames.join(\", \")}]`)\n console.log(`encoding: ${info.encoding}`)\n if (info.streams.length) console.log(`streams: ${info.streams.join(\" \")}`)\n console.log(`token: ${info.tokenRefreshed ? \"refreshed ✓\" : \"unchanged\"}`)\n console.groupEnd()\n /* eslint-enable no-console */\n}\n\n// Debug (issue #108): fold the response body #perform already read into the\n// trace — the stream action/target pairs and whether a token refresh arrived\n// (a boolean; the token VALUE is intentionally not stored). Shared by the\n// success and the non-OK-turbo-stream branches so both log the same shape.\nexport function recordBody(debug, body, freshToken) {\n debug.streams = debugStreams(body)\n debug.tokenRefreshed = freshToken != null\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "FE47EA9129EA3AE764756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "B6C25F611EB1EE0B64756E2164756E21",
9
9
  "names": []
10
10
  }