phlex-reactive 0.16.1 → 0.16.3
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -1
- data/README.md +33 -4
- data/app/javascript/phlex/reactive/features/bindings.js +37 -7
- data/app/javascript/phlex/reactive/features/bindings.min.js +2 -2
- data/app/javascript/phlex/reactive/features/bindings.min.js.map +3 -3
- data/app/javascript/phlex/reactive/inspect.js +4 -1
- data/app/javascript/phlex/reactive/inspect.min.js +2 -2
- data/app/javascript/phlex/reactive/inspect.min.js.map +3 -3
- data/app/javascript/phlex/reactive/reactive_controller.min.js +4 -4
- data/app/javascript/phlex/reactive/reactive_controller.min.js.map +3 -3
- data/lib/phlex/reactive/component/dsl.rb +3 -0
- data/lib/phlex/reactive/component/helpers.rb +32 -1
- data/lib/phlex/reactive/component/identity.rb +1 -1
- data/lib/phlex/reactive/component/registry.rb +15 -5
- data/lib/phlex/reactive/engine.rb +8 -1
- data/lib/phlex/reactive/param_schema.rb +5 -1
- data/lib/phlex/reactive/shareable.rb +18 -3
- data/lib/phlex/reactive/version.rb +1 -1
- metadata +1 -1
|
@@ -228,7 +228,8 @@ function ownedText(root, allRoots) {
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
// reactive_filter lives on the ROOT itself (the input/option/group/empty
|
|
231
|
-
// selectors it narrows client-side
|
|
231
|
+
// selectors it narrows client-side, and how it matches: issue #425's mode and
|
|
232
|
+
// fold). Null when the root declares no filter.
|
|
232
233
|
function readFilter(el) {
|
|
233
234
|
const input = el.getAttribute("data-reactive-filter-input")
|
|
234
235
|
if (!input) return null
|
|
@@ -237,5 +238,7 @@ function readFilter(el) {
|
|
|
237
238
|
option: el.getAttribute("data-reactive-filter-option"),
|
|
238
239
|
group: el.getAttribute("data-reactive-filter-group"),
|
|
239
240
|
empty: el.getAttribute("data-reactive-filter-empty"),
|
|
241
|
+
match: el.getAttribute("data-reactive-filter-match") ?? "substring",
|
|
242
|
+
fold: el.hasAttribute("data-reactive-filter-fold"),
|
|
240
243
|
}
|
|
241
244
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function m(t=document){let r=Array.from(t.querySelectorAll('[data-controller~="reactive"]'));return r.map((e)=>b(e,r))}function T(t=document){let r=m(t),e=globalThis.console;if(e&&typeof e.log==="function")e.log(`[phlex-reactive] ${r.length} reactive root(s)`);if(e&&typeof e.table==="function")e.table(r.map((a)=>({id:a.id,component:a.component??(a.opaque?"(opaque token)":null),actions:a.triggers.map((
|
|
1
|
+
function m(t=document){let r=Array.from(t.querySelectorAll('[data-controller~="reactive"]'));return r.map((e)=>b(e,r))}function T(t=document){let r=m(t),e=globalThis.console;if(e&&typeof e.log==="function")e.log(`[phlex-reactive] ${r.length} reactive root(s)`);if(e&&typeof e.table==="function")e.table(r.map((a)=>({id:a.id,component:a.component??(a.opaque?"(opaque token)":null),actions:a.triggers.map((n)=>n.action).join(", "),fields:a.fields.join(", "),computes:a.computes.join(", ")})));return r}function b(t,r){let e=t.getAttribute("data-reactive-token-value"),a=g(e);return{id:t.id||null,component:a.opaque?null:a.c??null,gid:a.gid??null,stateKeys:a.s?Object.keys(a.s):[],tokenVersion:a.v??null,opaque:!!a.opaque,status:v(t),triggers:h(t,r),clientOps:w(t,r),computes:q(t,r),fields:O(t,r),show:x(t,r),text:j(t,r),filter:S(t)}}function g(t){if(!t)return{opaque:!0};try{let r=t.split("--")[0],e=l(r),a=JSON.parse(e),n=a&&a._rails&&a._rails.data?a._rails.data:a;if(!n||typeof n!=="object")return{opaque:!0};return n}catch{return{opaque:!0}}}function l(t){return(globalThis.atob||((e)=>Buffer.from(e,"base64").toString("binary")))(t)}function v(t){return{error:t.getAttribute("data-reactive-error"),busy:t.getAttribute("aria-busy")==="true"||t.hasAttribute("data-reactive-busy"),dirty:t.hasAttribute("data-reactive-dirty")}}var A='[data-controller~="reactive"]';function y(t,r){return t.closest(A)===r}function o(t,r,e){return Array.from(t.querySelectorAll(r)).filter((a)=>y(a,t))}function h(t,r){return o(t,"[data-reactive-action-param]",r).map((e)=>({action:e.getAttribute("data-reactive-action-param"),event:d(e.getAttribute("data-action")),params:e.getAttribute("data-reactive-params-param"),debounce:e.getAttribute("data-reactive-debounce-param"),throttle:e.getAttribute("data-reactive-throttle-param"),confirm:e.getAttribute("data-reactive-confirm-param"),optimistic:e.getAttribute("data-reactive-optimistic-param"),loading:e.getAttribute("data-reactive-loading-param"),shortcut:e.getAttribute("data-reactive-shortcut-param")}))}function d(t){if(!t)return null;let r=t.indexOf("->");return r===-1?null:t.slice(0,r)}function w(t,r){return o(t,"[data-reactive-ops-param]",r).flatMap((e)=>{let a=e.getAttribute("data-reactive-ops-param"),n=d(e.getAttribute("data-action")),u=s(a);if(Array.isArray(u))return[{event:n,ops:u}];let c=(a??"").split(" ").map(s).filter((i)=>i&&typeof i==="object"&&!Array.isArray(i));if(c.length===0)return[{event:n,ops:a}];let p=(i)=>typeof i==="object"?`${i.keys}${i.inEditable?" editable":""}`:i,f=(i)=>`${i.on}${i.window?"@window":""}${i.shortcut==null?"":` shortcut=${p(i.shortcut)}`}`;return c.map((i)=>({event:i.on?f(i):n,ops:i.ops}))})}function s(t){try{return JSON.parse(t)}catch{return}}function q(t,r){return o(t,"[data-reactive-compute-reducer-param]",r).map((e)=>e.getAttribute("data-reactive-compute-reducer-param"))}function O(t,r){return o(t,"input[name], select[name], textarea[name]",r).map((e)=>e.getAttribute("name"))}function x(t,r){return o(t,"[data-reactive-show-field]",r).map((e)=>({field:e.getAttribute("data-reactive-show-field"),equals:e.getAttribute("data-reactive-show-equals"),not:e.getAttribute("data-reactive-show-not"),in:e.getAttribute("data-reactive-show-in")}))}function j(t,r){return o(t,"[data-reactive-text]",r).map((e)=>e.getAttribute("data-reactive-text"))}function S(t){let r=t.getAttribute("data-reactive-filter-input");if(!r)return null;return{input:r,option:t.getAttribute("data-reactive-filter-option"),group:t.getAttribute("data-reactive-filter-group"),empty:t.getAttribute("data-reactive-filter-empty"),match:t.getAttribute("data-reactive-filter-match")??"substring",fold:t.hasAttribute("data-reactive-filter-fold")}}export{T as report,m as scan};
|
|
2
2
|
|
|
3
|
-
//# debugId=
|
|
3
|
+
//# debugId=072DC7AC80C1193464756E2164756E21
|
|
4
4
|
//# sourceMappingURL=inspect.min.js.map
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["inspect.js"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"// The on-demand client inspector (issue #168) — the browser half of the\n// debugging toolkit, the client twin of `phlex_reactive:actions` / the MCP tools.\n//\n// It is a STANDALONE module (the confirm.js / compute.js precedent): it does NOT\n// touch the hot-path reactive_controller.js and costs NOTHING until you import\n// it. Loaded on demand from the console (the engine pins it), it scans the live\n// DOM and maps every reactive root + bound trigger back to the server\n// Component#action names — so you can answer \"what's reactive on this page, what\n// does each control POST, and which component owns it?\" without reading source.\n//\n// (await import(\"phlex/reactive/inspect\")).report() // console.table\n// const roots = (await import(\"phlex/reactive/inspect\")).scan() // data\n//\n// The server↔client mapping is BY NAME: scan()'s `component` + each trigger's\n// `action` are exactly the Component#action identifiers `phlex_reactive:actions`\n// and the MCP tools list. The docs page shows the two side by side.\n//\n// It reads the SAME DOM contract reactive_controller.js writes (roots\n// [data-controller~=\"reactive\"] with id + data-reactive-token-value; triggers\n// [data-reactive-action-param] with data-action/params/modifiers; client-only\n// [data-reactive-ops-param]; computes [data-reactive-compute-reducer-param]; the\n// show/filter/text binding families). It never mutates anything — pure read.\n\n// Scan `root` (default the whole document) and return one entry per reactive\n// root, deepest-scoped: each trigger is attributed to its NEAREST reactive root,\n// so a nested root doesn't double-count its parent's triggers.\nexport function scan(root = document) {\n const roots = Array.from(root.querySelectorAll('[data-controller~=\"reactive\"]'))\n return roots.map((el) => inspectRoot(el, roots))\n}\n\n// console.table the inventory (roots + a flattened trigger table), and return\n// the same data scan() does so `report()` is also usable programmatically. Safe\n// on an empty document (logs an empty table, throws nothing).\nexport function report(root = document) {\n const roots = scan(root)\n const console = globalThis.console\n\n // Guard each console method independently — a host page (or a test harness)\n // may ship a partial console, and report() must never throw over logging.\n if (console && typeof console.log === \"function\") {\n console.log(`[phlex-reactive] ${roots.length} reactive root(s)`)\n }\n if (console && typeof console.table === \"function\") {\n console.table(\n roots.map((r) => ({\n id: r.id,\n component: r.component ?? (r.opaque ? \"(opaque token)\" : null),\n actions: r.triggers.map((t) => t.action).join(\", \"),\n fields: r.fields.join(\", \"),\n computes: r.computes.join(\", \"),\n })),\n )\n }\n return roots\n}\n\n// --- one root --------------------------------------------------------------\n\nfunction inspectRoot(el, allRoots) {\n const token = el.getAttribute(\"data-reactive-token-value\")\n const payload = decodeToken(token)\n\n return {\n id: el.id || null,\n component: payload.opaque ? null : (payload.c ?? null),\n gid: payload.gid ?? null,\n stateKeys: payload.s ? Object.keys(payload.s) : [],\n tokenVersion: payload.v ?? null,\n opaque: !!payload.opaque,\n status: readStatus(el),\n triggers: ownedTriggers(el, allRoots),\n clientOps: ownedClientOps(el, allRoots),\n computes: ownedComputes(el, allRoots),\n fields: ownedFields(el, allRoots),\n show: ownedShow(el, allRoots),\n text: ownedText(el, allRoots),\n filter: readFilter(el),\n }\n}\n\n// The signed identity is base64(JSON)--signature under the Rails 7.1+ JSON\n// message serializer. Decode the segment BEFORE the \"--\", JSON-parse it, and\n// unwrap the MessageVerifier envelope ({_rails:{data:…}}) to the {c,gid,s,v}\n// payload. Degrade to { opaque: true } on anything that isn't JSON-decodable —\n// a Marshal-serialized payload (older apps) simply isn't readable client-side,\n// and that's fine: the inspector shows \"(opaque token)\" rather than throwing.\nfunction decodeToken(token) {\n if (!token) return { opaque: true }\n\n try {\n const segment = token.split(\"--\")[0]\n const json = atobUtf8(segment)\n const parsed = JSON.parse(json)\n const data = parsed && parsed._rails && parsed._rails.data ? parsed._rails.data : parsed\n if (!data || typeof data !== \"object\") return { opaque: true }\n return data\n } catch {\n return { opaque: true }\n }\n}\n\n// base64 → string. Uses the platform atob; a malformed/urlsafe segment throws,\n// which decodeToken catches and reports as opaque.\nfunction atobUtf8(b64) {\n const decode = globalThis.atob || ((s) => Buffer.from(s, \"base64\").toString(\"binary\"))\n return decode(b64)\n}\n\n// The at-a-glance runtime state the controller stamps on the root. Names/flags\n// only — never a value that could be sensitive.\nfunction readStatus(el) {\n return {\n error: el.getAttribute(\"data-reactive-error\"),\n busy: el.getAttribute(\"aria-busy\") === \"true\" || el.hasAttribute(\"data-reactive-busy\"),\n dirty: el.hasAttribute(\"data-reactive-dirty\"),\n }\n}\n\n// --- ownership scoping -----------------------------------------------------\n//\n// An element belongs to a root when its NEAREST reactive-root ancestor is that\n// root (matching the controller's own ownership walk). closest() finds the\n// nearest ancestor matching the selector; the element itself can be the root\n// (for a root that also carries a binding), so we start the walk from the\n// element and accept `el` as its own owner.\n\nconst ROOT_SELECTOR = '[data-controller~=\"reactive\"]'\n\nfunction ownedBy(node, root) {\n return node.closest(ROOT_SELECTOR) === root\n}\n\n// Query `root` for `selector`, keeping only elements whose nearest reactive\n// root IS `root` — so a nested root's matches are excluded here and attributed\n// to that inner root instead.\nfunction scopedQuery(root, selector, allRoots) {\n return Array.from(root.querySelectorAll(selector)).filter((node) => ownedBy(node, root))\n}\n\n// --- the binding families --------------------------------------------------\n\nfunction ownedTriggers(root, allRoots) {\n return scopedQuery(root, \"[data-reactive-action-param]\", allRoots).map((el) => ({\n action: el.getAttribute(\"data-reactive-action-param\"),\n event: eventOf(el.getAttribute(\"data-action\")),\n params: el.getAttribute(\"data-reactive-params-param\"),\n debounce: el.getAttribute(\"data-reactive-debounce-param\"),\n throttle: el.getAttribute(\"data-reactive-throttle-param\"),\n confirm: el.getAttribute(\"data-reactive-confirm-param\"),\n optimistic: el.getAttribute(\"data-reactive-optimistic-param\"),\n loading: el.getAttribute(\"data-reactive-loading-param\"),\n shortcut: el.getAttribute(\"data-reactive-shortcut-param\"),\n }))\n}\n\n// The Stimulus data-action descriptor is \"event->controller#method\"; the event\n// is the part before \"->\" (a bare \"controller#method\" has an implicit default\n// event, which we report as null).\nfunction eventOf(dataAction) {\n if (!dataAction) return null\n const arrow = dataAction.indexOf(\"->\")\n return arrow === -1 ? null : dataAction.slice(0, arrow)\n}\n\n// Client-only op triggers ([data-reactive-ops-param]) — a menu toggle, a DOM op\n// chain that never round-trips. Issue #271: the attr holds one on_client\n// binding record per call ({on, window?, ops, …}, space-joined when mix-ed),\n// reported as one { event, ops } entry each. A legacy [[op, args]] list keeps\n// the data-action prefix as its event; an undecodable attr is reported raw.\nfunction ownedClientOps(root, allRoots) {\n return scopedQuery(root, \"[data-reactive-ops-param]\", allRoots).flatMap((el) => {\n const raw = el.getAttribute(\"data-reactive-ops-param\")\n const event = eventOf(el.getAttribute(\"data-action\"))\n const legacy = parseJson(raw)\n if (Array.isArray(legacy)) return [{ event, ops: legacy }]\n const records = (raw ?? \"\").split(\" \").map(parseJson).filter((r) => r && typeof r === \"object\" && !Array.isArray(r))\n if (records.length === 0) return [{ event, ops: raw }]\n // A shortcut record (issue #357) reads as \"keydown@window shortcut=mod+z\".\n const shortcutOf = (s) => (typeof s === \"object\" ? `${s.keys}${s.inEditable ? \" editable\" : \"\"}` : s)\n const summary = (r) => `${r.on}${r.window ? \"@window\" : \"\"}${r.shortcut == null ? \"\" : ` shortcut=${shortcutOf(r.shortcut)}`}`\n return records.map((r) => ({ event: r.on ? summary(r) : event, ops: r.ops }))\n })\n}\n\n// JSON.parse that answers undefined instead of throwing — a malformed attr\n// must never break the scan.\nfunction parseJson(raw) {\n try {\n return JSON.parse(raw)\n } catch {\n return undefined\n }\n}\n\n// Compute reducers bound on this root ([data-reactive-compute-reducer-param]) —\n// the client-side data-binding twins of reactive_compute. Report reducer names.\nfunction ownedComputes(root, allRoots) {\n return scopedQuery(root, \"[data-reactive-compute-reducer-param]\", allRoots).map((el) =>\n el.getAttribute(\"data-reactive-compute-reducer-param\"),\n )\n}\n\n// The named form controls the dispatch would collect and send — the same\n// selector the controller's field walk uses. Names only, never values.\nfunction ownedFields(root, allRoots) {\n return scopedQuery(root, \"input[name], select[name], textarea[name]\", allRoots).map((el) =>\n el.getAttribute(\"name\"),\n )\n}\n\n// reactive_show bindings: which owned field controls each element's visibility.\nfunction ownedShow(root, allRoots) {\n return scopedQuery(root, \"[data-reactive-show-field]\", allRoots).map((el) => ({\n field: el.getAttribute(\"data-reactive-show-field\"),\n equals: el.getAttribute(\"data-reactive-show-equals\"),\n not: el.getAttribute(\"data-reactive-show-not\"),\n in: el.getAttribute(\"data-reactive-show-in\"),\n }))\n}\n\n// reactive_text spans (data-reactive-text=<name>) the client writes via\n// textContent — report the binding names.\nfunction ownedText(root, allRoots) {\n return scopedQuery(root, \"[data-reactive-text]\", allRoots).map((el) =>\n el.getAttribute(\"data-reactive-text\"),\n )\n}\n\n// reactive_filter lives on the ROOT itself (the input/option/group/empty\n// selectors it narrows client-side). Null when the root declares no filter.\nfunction readFilter(el) {\n const input = el.getAttribute(\"data-reactive-filter-input\")\n if (!input) return null\n return {\n input,\n option: el.getAttribute(\"data-reactive-filter-option\"),\n group: el.getAttribute(\"data-reactive-filter-group\"),\n empty: el.getAttribute(\"data-reactive-filter-empty\"),\n }\n}\n"
|
|
5
|
+
"// The on-demand client inspector (issue #168) — the browser half of the\n// debugging toolkit, the client twin of `phlex_reactive:actions` / the MCP tools.\n//\n// It is a STANDALONE module (the confirm.js / compute.js precedent): it does NOT\n// touch the hot-path reactive_controller.js and costs NOTHING until you import\n// it. Loaded on demand from the console (the engine pins it), it scans the live\n// DOM and maps every reactive root + bound trigger back to the server\n// Component#action names — so you can answer \"what's reactive on this page, what\n// does each control POST, and which component owns it?\" without reading source.\n//\n// (await import(\"phlex/reactive/inspect\")).report() // console.table\n// const roots = (await import(\"phlex/reactive/inspect\")).scan() // data\n//\n// The server↔client mapping is BY NAME: scan()'s `component` + each trigger's\n// `action` are exactly the Component#action identifiers `phlex_reactive:actions`\n// and the MCP tools list. The docs page shows the two side by side.\n//\n// It reads the SAME DOM contract reactive_controller.js writes (roots\n// [data-controller~=\"reactive\"] with id + data-reactive-token-value; triggers\n// [data-reactive-action-param] with data-action/params/modifiers; client-only\n// [data-reactive-ops-param]; computes [data-reactive-compute-reducer-param]; the\n// show/filter/text binding families). It never mutates anything — pure read.\n\n// Scan `root` (default the whole document) and return one entry per reactive\n// root, deepest-scoped: each trigger is attributed to its NEAREST reactive root,\n// so a nested root doesn't double-count its parent's triggers.\nexport function scan(root = document) {\n const roots = Array.from(root.querySelectorAll('[data-controller~=\"reactive\"]'))\n return roots.map((el) => inspectRoot(el, roots))\n}\n\n// console.table the inventory (roots + a flattened trigger table), and return\n// the same data scan() does so `report()` is also usable programmatically. Safe\n// on an empty document (logs an empty table, throws nothing).\nexport function report(root = document) {\n const roots = scan(root)\n const console = globalThis.console\n\n // Guard each console method independently — a host page (or a test harness)\n // may ship a partial console, and report() must never throw over logging.\n if (console && typeof console.log === \"function\") {\n console.log(`[phlex-reactive] ${roots.length} reactive root(s)`)\n }\n if (console && typeof console.table === \"function\") {\n console.table(\n roots.map((r) => ({\n id: r.id,\n component: r.component ?? (r.opaque ? \"(opaque token)\" : null),\n actions: r.triggers.map((t) => t.action).join(\", \"),\n fields: r.fields.join(\", \"),\n computes: r.computes.join(\", \"),\n })),\n )\n }\n return roots\n}\n\n// --- one root --------------------------------------------------------------\n\nfunction inspectRoot(el, allRoots) {\n const token = el.getAttribute(\"data-reactive-token-value\")\n const payload = decodeToken(token)\n\n return {\n id: el.id || null,\n component: payload.opaque ? null : (payload.c ?? null),\n gid: payload.gid ?? null,\n stateKeys: payload.s ? Object.keys(payload.s) : [],\n tokenVersion: payload.v ?? null,\n opaque: !!payload.opaque,\n status: readStatus(el),\n triggers: ownedTriggers(el, allRoots),\n clientOps: ownedClientOps(el, allRoots),\n computes: ownedComputes(el, allRoots),\n fields: ownedFields(el, allRoots),\n show: ownedShow(el, allRoots),\n text: ownedText(el, allRoots),\n filter: readFilter(el),\n }\n}\n\n// The signed identity is base64(JSON)--signature under the Rails 7.1+ JSON\n// message serializer. Decode the segment BEFORE the \"--\", JSON-parse it, and\n// unwrap the MessageVerifier envelope ({_rails:{data:…}}) to the {c,gid,s,v}\n// payload. Degrade to { opaque: true } on anything that isn't JSON-decodable —\n// a Marshal-serialized payload (older apps) simply isn't readable client-side,\n// and that's fine: the inspector shows \"(opaque token)\" rather than throwing.\nfunction decodeToken(token) {\n if (!token) return { opaque: true }\n\n try {\n const segment = token.split(\"--\")[0]\n const json = atobUtf8(segment)\n const parsed = JSON.parse(json)\n const data = parsed && parsed._rails && parsed._rails.data ? parsed._rails.data : parsed\n if (!data || typeof data !== \"object\") return { opaque: true }\n return data\n } catch {\n return { opaque: true }\n }\n}\n\n// base64 → string. Uses the platform atob; a malformed/urlsafe segment throws,\n// which decodeToken catches and reports as opaque.\nfunction atobUtf8(b64) {\n const decode = globalThis.atob || ((s) => Buffer.from(s, \"base64\").toString(\"binary\"))\n return decode(b64)\n}\n\n// The at-a-glance runtime state the controller stamps on the root. Names/flags\n// only — never a value that could be sensitive.\nfunction readStatus(el) {\n return {\n error: el.getAttribute(\"data-reactive-error\"),\n busy: el.getAttribute(\"aria-busy\") === \"true\" || el.hasAttribute(\"data-reactive-busy\"),\n dirty: el.hasAttribute(\"data-reactive-dirty\"),\n }\n}\n\n// --- ownership scoping -----------------------------------------------------\n//\n// An element belongs to a root when its NEAREST reactive-root ancestor is that\n// root (matching the controller's own ownership walk). closest() finds the\n// nearest ancestor matching the selector; the element itself can be the root\n// (for a root that also carries a binding), so we start the walk from the\n// element and accept `el` as its own owner.\n\nconst ROOT_SELECTOR = '[data-controller~=\"reactive\"]'\n\nfunction ownedBy(node, root) {\n return node.closest(ROOT_SELECTOR) === root\n}\n\n// Query `root` for `selector`, keeping only elements whose nearest reactive\n// root IS `root` — so a nested root's matches are excluded here and attributed\n// to that inner root instead.\nfunction scopedQuery(root, selector, allRoots) {\n return Array.from(root.querySelectorAll(selector)).filter((node) => ownedBy(node, root))\n}\n\n// --- the binding families --------------------------------------------------\n\nfunction ownedTriggers(root, allRoots) {\n return scopedQuery(root, \"[data-reactive-action-param]\", allRoots).map((el) => ({\n action: el.getAttribute(\"data-reactive-action-param\"),\n event: eventOf(el.getAttribute(\"data-action\")),\n params: el.getAttribute(\"data-reactive-params-param\"),\n debounce: el.getAttribute(\"data-reactive-debounce-param\"),\n throttle: el.getAttribute(\"data-reactive-throttle-param\"),\n confirm: el.getAttribute(\"data-reactive-confirm-param\"),\n optimistic: el.getAttribute(\"data-reactive-optimistic-param\"),\n loading: el.getAttribute(\"data-reactive-loading-param\"),\n shortcut: el.getAttribute(\"data-reactive-shortcut-param\"),\n }))\n}\n\n// The Stimulus data-action descriptor is \"event->controller#method\"; the event\n// is the part before \"->\" (a bare \"controller#method\" has an implicit default\n// event, which we report as null).\nfunction eventOf(dataAction) {\n if (!dataAction) return null\n const arrow = dataAction.indexOf(\"->\")\n return arrow === -1 ? null : dataAction.slice(0, arrow)\n}\n\n// Client-only op triggers ([data-reactive-ops-param]) — a menu toggle, a DOM op\n// chain that never round-trips. Issue #271: the attr holds one on_client\n// binding record per call ({on, window?, ops, …}, space-joined when mix-ed),\n// reported as one { event, ops } entry each. A legacy [[op, args]] list keeps\n// the data-action prefix as its event; an undecodable attr is reported raw.\nfunction ownedClientOps(root, allRoots) {\n return scopedQuery(root, \"[data-reactive-ops-param]\", allRoots).flatMap((el) => {\n const raw = el.getAttribute(\"data-reactive-ops-param\")\n const event = eventOf(el.getAttribute(\"data-action\"))\n const legacy = parseJson(raw)\n if (Array.isArray(legacy)) return [{ event, ops: legacy }]\n const records = (raw ?? \"\").split(\" \").map(parseJson).filter((r) => r && typeof r === \"object\" && !Array.isArray(r))\n if (records.length === 0) return [{ event, ops: raw }]\n // A shortcut record (issue #357) reads as \"keydown@window shortcut=mod+z\".\n const shortcutOf = (s) => (typeof s === \"object\" ? `${s.keys}${s.inEditable ? \" editable\" : \"\"}` : s)\n const summary = (r) => `${r.on}${r.window ? \"@window\" : \"\"}${r.shortcut == null ? \"\" : ` shortcut=${shortcutOf(r.shortcut)}`}`\n return records.map((r) => ({ event: r.on ? summary(r) : event, ops: r.ops }))\n })\n}\n\n// JSON.parse that answers undefined instead of throwing — a malformed attr\n// must never break the scan.\nfunction parseJson(raw) {\n try {\n return JSON.parse(raw)\n } catch {\n return undefined\n }\n}\n\n// Compute reducers bound on this root ([data-reactive-compute-reducer-param]) —\n// the client-side data-binding twins of reactive_compute. Report reducer names.\nfunction ownedComputes(root, allRoots) {\n return scopedQuery(root, \"[data-reactive-compute-reducer-param]\", allRoots).map((el) =>\n el.getAttribute(\"data-reactive-compute-reducer-param\"),\n )\n}\n\n// The named form controls the dispatch would collect and send — the same\n// selector the controller's field walk uses. Names only, never values.\nfunction ownedFields(root, allRoots) {\n return scopedQuery(root, \"input[name], select[name], textarea[name]\", allRoots).map((el) =>\n el.getAttribute(\"name\"),\n )\n}\n\n// reactive_show bindings: which owned field controls each element's visibility.\nfunction ownedShow(root, allRoots) {\n return scopedQuery(root, \"[data-reactive-show-field]\", allRoots).map((el) => ({\n field: el.getAttribute(\"data-reactive-show-field\"),\n equals: el.getAttribute(\"data-reactive-show-equals\"),\n not: el.getAttribute(\"data-reactive-show-not\"),\n in: el.getAttribute(\"data-reactive-show-in\"),\n }))\n}\n\n// reactive_text spans (data-reactive-text=<name>) the client writes via\n// textContent — report the binding names.\nfunction ownedText(root, allRoots) {\n return scopedQuery(root, \"[data-reactive-text]\", allRoots).map((el) =>\n el.getAttribute(\"data-reactive-text\"),\n )\n}\n\n// reactive_filter lives on the ROOT itself (the input/option/group/empty\n// selectors it narrows client-side, and how it matches: issue #425's mode and\n// fold). Null when the root declares no filter.\nfunction readFilter(el) {\n const input = el.getAttribute(\"data-reactive-filter-input\")\n if (!input) return null\n return {\n input,\n option: el.getAttribute(\"data-reactive-filter-option\"),\n group: el.getAttribute(\"data-reactive-filter-group\"),\n empty: el.getAttribute(\"data-reactive-filter-empty\"),\n match: el.getAttribute(\"data-reactive-filter-match\") ?? \"substring\",\n fold: el.hasAttribute(\"data-reactive-filter-fold\"),\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "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
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "072DC7AC80C1193464756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|