phlex-reactive 0.14.2 → 0.14.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.
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["features/bindings.js"],
4
4
  "sourcesContent": [
5
- "// phlex/reactive/features/bindings — what a form root wires at connect and\n// re-syncs after a morph, all of it client-only and all of it reading the\n// root's own controls: show bindings and cross-root show targets (issues\n// #161, #164, #176, #180, #209), completion bindings (#226), option filtering\n// (#163), the tag-chip input (#203) and draft nested-attribute rows with their\n// JSON mode (#208). One of the feature modules the opt-in client imports on\n// demand (issue #275); in the default client it is part of the one file.\n//\n// Grouped because they share a shape — a marker check at connect, one\n// delegated listener pair on the root, a seed, the same pass again on\n// turbo:morph-element — and a page: a form with a show binding is the form\n// that filters options and adds rows. The seeds here write visible state\n// (and the JSON mode writes a hidden field a request collects), so with the\n// opt-in client the feature `gates`: a root's requests wait for it.\n//\n// The conditional confirm (issue #179) lives here too: its condition is the\n// same DNF fold a show binding uses, and its `predicate:` form reads the\n// confirm_predicate seam — imported here by its bare name, so the runtime no\n// longer imports it. A plain string confirm: never comes here; a conditional\n// one on the opt-in client waits for this module before its dialog (a root\n// with one preloads it).\n//\n// This module never imports the runtime: it reaches a controller through the\n// `core` handle.\nimport { confirmPredicate } from \"phlex/reactive/confirm_predicate\"\n\n// controller -> its context: the root, the core handle, and what used to be\n// the controller's own fields for these features. Every field it does not\n// set here starts undefined (falsy); a property name is one thing the\n// minifier never renames, so the record carries no more of them than it\n// must (issue #310).\nconst contexts = new WeakMap()\n// root element -> the same context, for the check_group op (issue #342),\n// which names a root, not a controller.\nconst rootContexts = new WeakMap()\n\nfunction ctx(controller, core) {\n let c = contexts.get(controller)\n if (!c) {\n contexts.set(controller, (c = { root: controller.element, core, nestedIndex: 0 }))\n if (c.root) rootContexts.set(c.root, c)\n }\n return c\n}\n\n// Add a delegated listener on the root for each of the space-separated\n// `types`, and keep its removal for disconnect().\nfunction listen(c, types, handler) {\n for (const type of types.split(\" \")) {\n c.root.addEventListener?.(type, handler)\n c.offs.push(() => c.root.removeEventListener?.(type, handler))\n }\n}\n\n// What connect() wires is one seed per binding: it runs now, again on\n// turbo:morph-element, and again from reseed().\nfunction seed(c, run) {\n listen(c, \"turbo:morph-element\", run)\n c.seeds.push(run)\n run()\n}\n\nexport function connect(controller, core) {\n const c = ctx(controller, core)\n c.offs = []\n c.seeds = []\n\n // Show bindings (issue #161) — ONLY when this root owns one, so a component\n // without any pays a single probe (the dirty-tracking gate precedent). ONE\n // delegated listener pair on the root (input + change bubble from every\n // owned field — no per-field wiring, and a reactive_compute output write\n // dispatches a real input event, so computed values drive visibility too).\n // The connect sync seeds the initial state — a plain replace re-connects —\n // and turbo:morph-element re-syncs after an in-place morph (which keeps the\n // element connected, fires no Stimulus lifecycle, and may preserve a\n // user-edited field value the server's hidden attrs don't reflect).\n if (showSyncEnabled(c)) {\n // A select-all header's edit flips its group first (issue #319) — on\n // `input`, which a click fires BEFORE `change`, or the pass below would\n // reset the header to its group's state first (the flip is idempotent, so\n // the `change` that follows flips nothing). The change events it\n // dispatches re-enter here and are skipped while it flips, so the pass\n // after the loop runs once, not once per box.\n c.show = (event) => {\n if (c.flipping) return\n // Only a user edit pushes the header onto its group; a morph (whose\n // turbo:morph-element bubbles from the header too) derives it instead.\n if (event?.type === \"input\" || event?.type === \"change\") flipGroup(c, event.target)\n // Whether this root has group bindings is read at connect and again on\n // a morph (which may add one, and so need the observer) — never per\n // keystroke.\n else {\n c.groups = hasGroups(c)\n observeGroups(c)\n }\n syncShow(c)\n }\n listen(c, \"input change\", c.show)\n seed(c, c.show)\n }\n\n // Completion bindings (issue #226) — ONLY when the root declares\n // data-reactive-on-complete (the show/filter gate precedent). ONE\n // delegated input+change listener evaluates every binding's DNF over the\n // owned fields and runs its ops on the RISING EDGE — the event-driven\n // flip to true. The connect pass ARMS without firing (a fresh render with\n // already-satisfied conditions must never self-fire — the $ops seed\n // precedent), and turbo:morph-element re-arms the same way after an\n // in-place morph. Listeners added HERE run after the Stimulus-wired\n // recompute delegation for the same event, so the evaluation reads\n // compute-NORMALIZED values (and a compute output write dispatches a real\n // input event that re-evaluates anyway).\n if (onCompleteEnabled(c)) {\n listen(c, \"input change\", (event) => syncOnComplete(c, event))\n seed(\n c,\n (c.arm = () => {\n syncOnComplete(c, null)\n observeGroups(c)\n }),\n )\n }\n\n // A form reset (issue #342) restores its fields AFTER the `reset` event\n // dispatches (and only if nothing cancelled it), so this waits a task, then\n // re-syncs the bindings and re-arms the completion latches without firing\n // (a reset fires no input/change, so a checked-count latch would stay high).\n if (c.show || c.arm) {\n listen(c, \"reset\", () =>\n setTimeout(() => {\n if (contexts.get(controller) !== c) return\n c.show?.()\n c.arm?.()\n }, 0),\n )\n }\n\n // Option filtering (issue #163) — ONLY when the root declares the binding\n // (reactive_filter emits both attrs together), so a component without one\n // pays two attribute reads. ONE delegated input listener on the root — the\n // handler re-filters only for events from the NAMED input, so keystrokes in\n // unrelated fields on a wide form never pay a filter pass. The connect sync\n // seeds from the input's current value (a plain replace re-connects; back\n // navigation may restore typed text), and turbo:morph-element re-applies\n // after an in-place morph (which keeps the element connected, fires no\n // Stimulus lifecycle, and may preserve the user's typed query while the\n // server re-rendered every option visible).\n if (filterEnabled(c)) {\n listen(c, \"input\", (event) => filterInputEvent(c, event) && syncFilter(c))\n seed(c, () => syncFilter(c))\n }\n\n // Tag-chip input (issue #203) — ONLY when the root names the hidden value\n // field (reactive_tags), so a component without one pays one attribute\n // read. The chip list is a CLIENT PROJECTION of the hidden field's\n // comma-joined value: connect seeds it (a plain replace re-connects with\n // the server-rendered value), and turbo:morph-element re-projects after an\n // in-place morph (the morph wrote server truth into the hidden field while\n // the chips DOM kept the pre-morph projection). Registered AFTER the\n // filter's listeners so a morph re-filters first and the tags pass then\n // re-marks selected options on the fresh visibility state.\n if (tagsEnabled(c)) seed(c, () => syncTags(c))\n\n // JSON-mode nested rows (issue #208) — ONLY when the root owns a list with\n // `as: :json`, so a form without one pays a single probe (the show/filter/\n // tags gate precedent). ONE delegated input + change listener re-serializes\n // the rows into the hidden field on every owned edit (nestedAdd/Remove call\n // the sync directly; this covers typing into a row's fields). The connect\n // seed writes the initial array (a plain replace re-connects), and\n // turbo:morph-element re-seeds after an in-place morph (which keeps the\n // element connected, fires no Stimulus lifecycle, and may have rewritten\n // the rows to server truth while the hidden field kept its pre-morph value).\n if (nestedJsonEnabled(c)) {\n listen(c, \"input change\", (event) => syncNestedJson(c, event))\n seed(c, () => syncAllNestedJson(c))\n }\n}\n\n// Remove every listener connect() added, so a stray event after a Turbo\n// morph/navigation never re-evaluates against a detached root.\nexport function disconnect(controller) {\n const c = contexts.get(controller)\n if (!c) return\n contexts.delete(controller)\n rootContexts.delete(c.root)\n for (const off of c.offs ?? []) off()\n c.groupObserver?.disconnect()\n}\n\n// Re-run the seeds that read field values, in connect() order — for a\n// feature that changed those values after connect() ran (the draft restore).\n// Each is the same re-sync a morph runs; on-complete re-ARMS without firing.\nexport function reseed(controller) {\n for (const run of contexts.get(controller)?.seeds ?? []) run()\n}\n\n// A value this module wrote is announced the way a user's edit is: a bubbling\n// `input` (the set-value + dispatch contract, issue #183), so dirty tracking,\n// reactive_show and compute all see it.\nfunction fireInput(el) {\n if (typeof el.dispatchEvent === \"function\") el.dispatchEvent(new Event(\"input\", { bubbles: true }))\n}\n\n// The Stimulus actions this module answers (the controller's own methods of\n// the same names hand the event here). tagsAdd, tagsPick, tagsRemove,\n// nestedAdd and nestedRemove take the controller themselves; the JSON sync is\n// also the root's own listener, so it keeps its context form inside.\nfunction publicSyncNestedJson(controller, core, event) {\n return syncNestedJson(ctx(controller, core), event)\n}\nexport { publicSyncNestedJson as syncNestedJson }\n\n// The runtime's call-in for a conditional confirm: the message when its\n// condition fires over this root's fields, else null.\nexport function confirmMessage(controller, core, confirmWhen) {\n return conditionalConfirmMessage(ctx(controller, core), confirmWhen)\n}\n\n// An id-only selector (the issue #159/#164 allowlist for anything that\n// escapes the root).\nconst ID_SELECTOR = /^#[A-Za-z_][\\w-]*$/\n\n// The selector matching every OWNED-element show binding: single-field\n// (data-reactive-show-field, issue #161) OR compound all:/any:\n// (data-reactive-show, issue #176). Both the connect() gate and the sync walk\n// use it so a compound-only root still enables the sync.\nconst SHOW_BINDING_SELECTOR = \"[data-reactive-show-field], [data-reactive-show]\"\n\n// Evaluate a show binding's declared literal predicate (issue #161) against\n// the controlling field's current value. Exactly one of the three predicate\n// attrs decides: equals (value === literal), not (value !== literal), in\n// (value ∈ a JSON string list). The vocabulary is fixed and literal-only —\n// never an expression, so there is no eval surface (the reactive_show helper\n// enforces the same shape loudly at render; this is the client half of the\n// two-sided posture). Returns true/false for a decidable binding, or null for\n// a malformed/missing predicate — the caller SKIPS a null so a hand-built or\n// stale binding never flips visibility it doesn't understand (default-deny,\n// like the op whitelist).\nfunction showBindingMatches(el, value) {\n const equals = el.getAttribute(\"data-reactive-show-equals\")\n if (equals !== null) return value === equals\n const not = el.getAttribute(\"data-reactive-show-not\")\n if (not !== null) return value !== not\n const inRaw = el.getAttribute(\"data-reactive-show-in\")\n if (inRaw !== null) {\n try {\n const list = JSON.parse(inRaw)\n if (Array.isArray(list)) return list.includes(value)\n } catch {\n // fall through to the warn below — malformed JSON and a non-array both skip\n }\n console.warn(`[phlex-reactive] malformed reactive_show in: list ${JSON.stringify(inRaw)} — skipped`)\n return null\n }\n // Numeric threshold predicates (issue #176 part B): gte/gt/lte/lt read the\n // literal off its own flat attr and compare Number(value) against it. Any\n // present numeric attr decides the binding — a non-numeric field value (NaN)\n // is false (hidden), and a non-numeric LITERAL warn-skips (null).\n for (const key of SHOW_NUMERIC_KEYS) {\n const raw = el.getAttribute(`data-reactive-show-${key}`)\n if (raw !== null) return numericPredicateMatches(key, raw, value)\n }\n console.warn(\"[phlex-reactive] a reactive_show binding declares no predicate — skipped\")\n return null\n}\n\n// Parse a compound show binding's JSON payload (issue #176 part A). Malformed\n// JSON degrades to null WITH a warn — a bad binding must never throw or blank\n// the page (client-side default-deny), but a collision (two bindings' JSON\n// mix-joined) is worth surfacing.\nfunction parseShowCompound(raw) {\n try {\n const parsed = JSON.parse(raw)\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) return parsed\n } catch {\n // fall through to the warn\n }\n console.warn(`[phlex-reactive] malformed compound reactive_show payload ${JSON.stringify(raw)} — skipped`)\n return null\n}\n\n// Parse a data-reactive-on-complete payload (issue #226): a JSON array of\n// { any: [[term, …], …], ops: [[op, args], …] } bindings. Malformed JSON, a\n// non-array, or a binding missing either half degrades to [] WITH a warn — a\n// bad payload must never throw or fire an op (client-side default-deny, the\n// parseShowCompound posture).\nfunction parseOnComplete(raw) {\n try {\n const list = JSON.parse(raw)\n if (\n Array.isArray(list) &&\n list.every((b) => b && typeof b === \"object\" && Array.isArray(b.any) && Array.isArray(b.ops))\n ) {\n return list\n }\n } catch {\n // fall through to the warn\n }\n console.warn(`[phlex-reactive] malformed reactive_on_complete payload ${JSON.stringify(raw)} — skipped`)\n return []\n}\n\n// Route a parsed data-reactive-show payload to the right evaluator. The 0.10\n// wire is { any: [ [term,…], … ] } (DNF — groups are ARRAYS). For a stale tab\n// still serving pre-0.10 HTML (deploy overlap), fall back to the 0.9.5 compound\n// shape { all: [term,…] } / { any: [term,…] } where the values are flat TERM\n// OBJECTS, not arrays. The nesting distinguishes them: DNF's any[0] is an Array.\n// DELETE the legacy arm in 0.11.\nfunction showPayloadMatches(payload, fieldValue) {\n if (!payload || typeof payload !== \"object\") return null\n const any = payload.any\n if (Array.isArray(any) && (any.length === 0 || Array.isArray(any[0]))) {\n return anyOfAllsMatches(any, fieldValue)\n }\n return legacyCompoundShowMatches(payload, fieldValue)\n}\n\n// LEGACY (0.9.5, deploy-overlap only — DELETE in 0.11): the flat all:/any:\n// compound fold, where terms are objects (not groups). Preserved so a morph of\n// stale pre-0.10 HTML doesn't go dead.\nfunction legacyCompoundShowMatches(payload, fieldValue) {\n const connective = Array.isArray(payload.all) ? \"all\" : Array.isArray(payload.any) ? \"any\" : null\n if (!connective) return null\n const terms = payload[connective]\n if (terms.length === 0) return null\n const results = terms.map((term) => dnfTermMatches(term, fieldValue))\n return connective === \"all\" ? results.every(Boolean) : results.some(Boolean)\n}\n\n// A cross-root show target must be a single ID selector (issue #164) — the\n// SAME shape the #159 mirror enforces (one shared regex), with its own warn so\n// a refused show target is distinguishable in the console. The client half of\n// the two-sided default-deny: reactive_show_targets raises at declare time; a\n// hand-built wire attr must not widen the escape to class/compound selectors.\n// A refused selector warns + skips — its siblings still apply. The group\n// targets (issue #343) share it, naming their own kind in the warn.\nfunction guardShowTargetSelector(selector, kind = \"show\") {\n if (typeof selector === \"string\" && ID_SELECTOR.test(selector)) return true\n console.warn(`[phlex-reactive] refused cross-root ${kind} target ${JSON.stringify(selector)} — skipped`)\n return false\n}\n\n\n// Whether this root owns a show binding (issue #161) or declares cross-root\n// show targets (issue #164) — the connect() gate, so a component with\n// neither pays only this probe (the #dirtyTrackingEnabled precedent). A\n// NESTED root's bindings don't count: its own controller instance syncs them\n// (issue #15 ownership). The targets attr is checked FIRST — one\n// getAttribute, cheaper than the binding walk.\nfunction showSyncEnabled(c) {\n if (c.root.getAttribute?.(\"data-reactive-show-targets\")) return true\n if (c.root.getAttribute?.(\"data-reactive-group-targets\")) return true\n // Single-field bindings carry -field; compound all:/any: bindings (issue\n // #176) carry data-reactive-show and have NO single controlling field, so\n // both selectors gate the sync.\n const nodes = c.root.querySelectorAll?.(SHOW_BINDING_SELECTOR) ?? []\n for (const el of nodes) if (c.core.owns(el)) return true\n for (const el of c.root.querySelectorAll?.(GROUP_BINDING_SELECTOR) ?? []) if (c.core.owns(el)) return true\n return false\n}\n\n// The connect() gate for completion bindings (issue #226) — one attribute read.\nfunction onCompleteEnabled(c) {\n return !!c.root.getAttribute?.(\"data-reactive-on-complete\")\n}\n\n// Parse-and-memoize the completion bindings, keyed on the RAW attr string:\n// a morph that rewrote the payload re-parses and RESETS the latches (the\n// morph listener's own arm pass then re-arms without firing). A removed\n// attr yields [] silently; malformed JSON warns (in parseOnComplete).\nfunction onCompleteBindings(c) {\n const raw = c.root.getAttribute?.(\"data-reactive-on-complete\") ?? null\n if (raw !== c.onCompleteRaw) {\n c.onCompleteRaw = raw\n c.onCompleteParsed = raw == null ? [] : parseOnComplete(raw)\n c.onCompleteStates = c.onCompleteParsed.map(() => false)\n }\n return c.onCompleteParsed\n}\n\n// Evaluate every completion binding (issue #226) over the owned fields —\n// the SAME memoized, scope-aware field resolver and DNF fold the show sync\n// uses — and run each binding's ops on ITS OWN rising edge. `event` null\n// (the connect/morph arm pass) updates the latches WITHOUT firing, so ops\n// only ever run from a real user gesture. An undecidable payload (no\n// groups) leaves its latch alone — default-deny, like every malformed-wire\n// arm. Ops resolve through runOps's root-scoped targets; a missing to:\n// defaults to \"@root\" (the $ops convention).\nfunction syncOnComplete(c, event) {\n const bindings = onCompleteBindings(c)\n if (!bindings.length) return\n\n const fieldValue = fieldResolver(c, c.core.ownership())\n bindings.forEach((binding, i) => {\n const matches = anyOfAllsMatches(binding.any, fieldValue)\n if (matches === null) return\n const fire = matches && !c.onCompleteStates[i] && Boolean(event)\n c.onCompleteStates[i] = matches\n if (fire) {\n c.core.applyOps(binding.ops, \"@root\")\n }\n })\n}\n\n// Re-evaluate every OWNED show binding in one pass (issue #161): read the\n// controlling field's current value, evaluate the declared literal predicate,\n// toggle `hidden`. A full pass (not per-target) for the same reason as\n// #scanDirty — a radio group's deselected radio fires no event — and because\n// several bindings can hang off one field (the value read is memoized per\n// pass). A binding whose field can't be resolved, or whose predicate is\n// malformed, leaves visibility ALONE — a bad binding must never break or\n// blank the page (client-side default-deny).\nfunction syncShow(c) {\n if (typeof c.root?.querySelectorAll !== \"function\") return\n\n const owns = c.core.ownership()\n const scope = c.root.getAttribute?.(\"data-reactive-scope\") || null\n // A memoized resolver shared by every binding in this pass — a field driving\n // several bindings (and several DNF terms) reads exactly once. Scope-aware:\n // a bare field `director` resolves as `[name=\"scope[director]\"]` (issue #180).\n const fieldValue = fieldResolver(c, owns)\n for (const el of c.root.querySelectorAll(SHOW_BINDING_SELECTOR)) {\n if (!owns(el)) continue // a nested root's binding is its own controller's job\n\n // The 0.10 DNF payload (issue #180): data-reactive-show carries\n // { any: [ [term,…], … ] }. The legacy flat-attr and 0.9.5-compound read\n // arms live in showPayloadMatches/showBindingMatches for deploy overlap.\n const payloadRaw = el.getAttribute(\"data-reactive-show\")\n if (payloadRaw !== null) {\n const match = showPayloadMatches(parseShowCompound(payloadRaw), fieldValue)\n if (match !== null) applyShowVisibility(el, match, owns, scope)\n continue\n }\n\n // LEGACY flat-attr binding (pre-0.10, deploy overlap — removed in 0.11).\n const name = el.getAttribute(\"data-reactive-show-field\")\n if (!name) continue\n const value = fieldValue(name)\n if (value === null) continue // no owned field with that name — leave it be\n const match = showBindingMatches(el, value)\n if (match === null) continue // malformed predicate — warned + skipped\n applyShowVisibility(el, match, owns, scope)\n }\n\n // The group bindings (issue #319) and the cross-root pass (issue #164)\n // share the same owned-field memo, so a field driving several reads once.\n if (c.groups) syncGroups(c, fieldValue, owns, scope)\n syncShowTargets(c, fieldValue)\n syncGroupTargets(c, fieldValue)\n}\n\n// Toggle `hidden` (and, when the binding declares data-reactive-show-disable,\n// the `disabled` of every owned named control inside it) from a match result\n// (issue #180). Disabling a hidden section's controls stops them submitting —\n// the stale-value fix. A visible section re-enables them. Controls a nested\n// reactive root owns are left alone (#15 ownership).\nfunction applyShowVisibility(el, match, owns, scope) {\n el.hidden = !match\n if (el.getAttribute(\"data-reactive-show-disable\") !== \"true\") return\n if (typeof el.querySelectorAll !== \"function\") return\n for (const control of el.querySelectorAll(\"input[name], select[name], textarea[name]\")) {\n if (owns(control)) control.disabled = !match\n }\n // The element itself may be a named control (a bare field with a binding).\n if (el.name && owns(el)) el.disabled = !match\n}\n\n// Apply the declared cross-root show targets (issue #164) — the visibility\n// parallel of #applyComputeMirrors. For each declared field: read the OWNED\n// field's current value (never a nested root's — you can only drive outside\n// visibility from a field this root owns), then for each \"#id\" → predicate\n// entry: guard the selector id-only (warn-and-skip; the Ruby helper raised\n// at declare time — two-sided default-deny), resolve it DOCUMENT-WIDE, and\n// toggle `hidden`. A target id not on the page is silently skipped (an\n// unrendered tab pane is normal); a malformed predicate warn-skips its one\n// target while siblings still apply. With no map declared this is one\n// getAttribute and out.\n//\n// A \"#id\" KEY (issue #209) is a TARGET-KEYED entry instead: its value is the\n// same DNF payload data-reactive-show holds, folded with per-term owned-field\n// reads — the multi-field cross-root case. The \"#\" prefix routes unambiguously\n// (a field name may never start with \"#\"; the Ruby helper raises).\nfunction syncShowTargets(c, fieldValue) {\n const map = parseShowTargets(c)\n for (const [name, targets] of Object.entries(map)) {\n if (name.startsWith(\"#\")) {\n applyConditionsTarget(name, targets, fieldValue)\n continue\n }\n if (!targets || typeof targets !== \"object\" || Array.isArray(targets)) continue\n const value = fieldValue(name)\n if (value === null) continue // no owned field with that name — leave them be\n // Every target's terms share this one field, so a constant resolver folds\n // the group (issue #180): a target's value is a DNF GROUP (terms ANDed).\n // (A checked_* term asks for the group's count instead, issue #319.)\n const resolve = (_, count) => (count ? fieldValue(name, true) : value)\n for (const [selector, group] of Object.entries(targets)) {\n if (!guardShowTargetSelector(selector)) continue\n // 0.10 wire: the value is a DNF GROUP (an array of terms, ANDed).\n // LEGACY (0.9.5, deploy overlap — DELETE in 0.11): a flat predicate\n // OBJECT ({ equals/not/in/gte… }) routed through showPredicateMatches.\n let match\n if (Array.isArray(group)) {\n if (group.length === 0) {\n console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${selector} — skipped`)\n continue\n }\n match = group.every((term) => dnfTermMatches(term, resolve))\n } else {\n const legacy = showPredicateMatches(group, value)\n if (legacy === null) {\n console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${selector} — skipped`)\n continue\n }\n match = legacy\n }\n for (const node of document.querySelectorAll(selector)) node.hidden = !match\n }\n }\n}\n\n// Apply ONE target-keyed conditions entry (issue #209): \"#id\" → the DNF\n// payload { any: [[term,…],…] }, folded by the SAME anyOfAllsMatches as an\n// in-root reactive_show — each term reads its OWN owned field, a missing\n// owned field reads as blank (fail-closed, the shared-fixture contract). A\n// target whose referenced fields are ALL unowned is left alone — the\n// single-field skip generalized (this root has nothing to evaluate with). A\n// malformed payload warn-skips its one target while siblings still apply;\n// the selector guard is the same id-only allowlist as every cross-root arm.\nfunction applyConditionsTarget(selector, payload, fieldValue) {\n if (!guardShowTargetSelector(selector)) return\n const groups = payload && typeof payload === \"object\" && !Array.isArray(payload) ? payload.any : null\n const fields = dnfGroupFields(groups)\n if (fields === null) {\n console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${selector} — skipped`)\n return\n }\n if (fields.every((name) => fieldValue(name) === null)) return // no owned field — leave it be\n const match = anyOfAllsMatches(groups, fieldValue)\n if (match === null) return // unreachable after dnfGroupFields, kept fail-closed\n for (const node of document.querySelectorAll(selector)) node.hidden = !match\n}\n\n// The declared cross-root show-target map (issue #164): a JSON object of\n// { field: { \"#id\": predicate } } from data-reactive-show-targets (emitted\n// by reactive_show_targets on the root). Absent degrades to {}; malformed\n// degrades to {} WITH a warn — never a throw (the #parseComputeMirror\n// contract), but never silent either: the likeliest cause is TWO\n// reactive_show_targets calls on one root, whose JSON strings Phlex `mix`\n// space-joined into an unparseable attr. The warn names the fix.\nfunction parseShowTargets(c, kind = \"show\", fix = \"mode: { ... }, kind: { ... }\") {\n const raw = c.root.getAttribute?.(`data-reactive-${kind}-targets`)\n if (!raw) return {}\n try {\n const parsed = JSON.parse(raw)\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) return parsed\n } catch {\n // fall through to the shared warn below\n }\n console.warn(\n `[phlex-reactive] malformed data-reactive-${kind}-targets — ignored. ` +\n `Did two reactive_${kind}_targets calls collide on one root? Declare every entry in ONE call: ` +\n `reactive_${kind}_targets(${fix})`\n )\n return {}\n}\n\n// The current value of the OWNED field controlling a show binding, as the\n// string the literal predicate compares against. Mirrors #collectFields'\n// per-kind reads: a checkbox reports its checked state (\"true\"/\"false\" — its\n// .value is the constant \"on\", and the checkbox wins over the hidden input\n// Rails pairs with it); a radio group reports the CHECKED radio's value (\"\"\n// when none is); anything else reports .value first-wins. Returns null when\n// no owned field carries the name — the caller then leaves visibility alone.\nfunction showFieldValue(c, name, owns, scope) {\n let sawRadio = false\n let first = null\n for (const el of c.root.querySelectorAll(namedSelector(name, scope))) {\n if (!owns(el)) continue\n if (el.type === \"checkbox\") return el.checked ? \"true\" : \"false\"\n if (el.type === \"radio\") {\n if (el.checked) return el.value ?? \"\"\n sawRadio = true\n continue\n }\n first ??= el\n }\n if (first) return first.value ?? \"\"\n return sawRadio ? \"\" : null\n}\n\n// Scope (issue #180): a bare field `director` under `data-reactive-scope=\n// \"form\"` resolves as `[name=\"form[director]\"]`. A name already carrying a\n// bracket (a raw wire name the author passed, like \"ids[]\") is used verbatim.\nfunction namedSelector(name, scope) {\n return `[name=\"${scope && !name.includes(\"[\") ? `${scope}[${name}]` : name}\"]`\n}\n\n// The per-pass field resolver every DNF fold reads (show, enable, on-complete).\n// fieldValue(name) is the field's string value; fieldValue(name, true) is how\n// many owned boxes of that checkbox group are ticked (the checked_* terms,\n// issue #319). Memoized, so a field driving several bindings reads once.\nfunction fieldResolver(c, owns) {\n const scope = c.root.getAttribute?.(\"data-reactive-scope\") || null\n const values = new Map()\n return (name, count) => {\n const key = count ? `#${name}` : name\n if (!values.has(key)) {\n values.set(key, count ? groupBoxes(c, name, owns, scope).filter((box) => box.checked).length : showFieldValue(c, name, owns, scope))\n }\n return values.get(key)\n }\n}\n\n// --- Bulk selection (issue #319) ---------------------------------------------\n// A checkbox GROUP is the owned boxes sharing one name. Three bindings read it:\n// the select-all header (data-reactive-select-all), the ticked count\n// (data-reactive-count), and the checked_* terms any condition can use —\n// reactive_enable (data-reactive-enable) flips the element's own `disabled`\n// from one. They re-sync with the show pass: on every input/change, on a\n// morph, and when boxes are added or removed (observeGroups).\nconst GROUP_BINDING_SELECTOR = \"[data-reactive-enable], [data-reactive-select-all], [data-reactive-count]\"\n\n// Whether the root holds any group binding (owned or not — a stray nested\n// one only costs the walk, which still skips it).\nfunction hasGroups(c) {\n return !!c.root.querySelector?.(GROUP_BINDING_SELECTOR)\n}\n\n// The owned checkboxes of a group (a nested root's boxes are its own, #15).\nfunction groupBoxes(c, name, owns, scope) {\n return [...c.root.querySelectorAll(namedSelector(name, scope))].filter((el) => el.type === \"checkbox\" && owns(el))\n}\n\n// A header's edit ticks or unticks every owned box of its group (the header\n// itself is never a member). The show listener syncs once afterwards.\nfunction flipGroup(c, header) {\n const group = header?.getAttribute?.(\"data-reactive-select-all\")\n if (group && c.core.owns(header)) setGroup(c, group, header.checked, header)\n}\n\n// The check_group op (issue #342): set a root's group the way its header\n// would, then sync its bindings once. A root this module never connected (no\n// group binding, only the boxes) still flips, with the #15 ownership check.\nexport function checkGroup(root, group, checked) {\n if (!group) return\n const c = rootContexts.get(root) ?? {\n root,\n core: { ownership: () => (el) => el.closest('[data-controller~=\"reactive\"]') === root },\n }\n setGroup(c, group, checked !== false)\n c.show?.()\n}\n\n// Tick or untick every owned box of a group, and dispatch `input` + `change`\n// on each one it flips so computes, shows and on-complete bindings see a real\n// edit. The re-entrant events are skipped by the show listener while this\n// runs (c.flipping); the caller syncs once afterwards.\nfunction setGroup(c, group, checked, except) {\n const scope = c.root.getAttribute?.(\"data-reactive-scope\") || null\n const flipped = groupBoxes(c, group, c.core.ownership(), scope).filter((box) => box !== except && box.checked !== checked)\n // Every box first, then the events: a listener (a checked-count\n // on_complete) must see the group's final count, never a half-flipped one.\n for (const box of flipped) box.checked = checked\n c.flipping = true\n try {\n for (const box of flipped) {\n // input then change, as a click does: computes listen on input.\n for (const type of [\"input\", \"change\"]) box.dispatchEvent?.(new Event(type, { bubbles: true }))\n }\n } finally {\n c.flipping = false\n }\n}\n\n// One pass over the owned group bindings: enable from its conditions, the\n// count as text (change-guarded, like #mirrorText), and each header's\n// checked/indeterminate from its group (the header itself never counts).\nfunction syncGroups(c, fieldValue, owns, scope) {\n for (const el of c.root.querySelectorAll(GROUP_BINDING_SELECTOR)) {\n if (!owns(el)) continue\n const enable = el.getAttribute(\"data-reactive-enable\")\n if (enable !== null) {\n const match = showPayloadMatches(parseShowCompound(enable), fieldValue)\n if (match !== null) el.disabled = !match\n }\n const counted = el.getAttribute(\"data-reactive-count\")\n if (counted) {\n const text = String(fieldValue(counted, true))\n if (el.textContent !== text) el.textContent = text\n }\n const group = el.getAttribute(\"data-reactive-select-all\")\n if (group) {\n const boxes = groupBoxes(c, group, owns, scope).filter((box) => box !== el)\n const ticked = boxes.filter((box) => box.checked).length\n el.checked = ticked > 0 && ticked === boxes.length\n el.indeterminate = ticked > 0 && ticked < boxes.length\n }\n }\n}\n\n// The declared cross-root group targets (issue #343) — the count and enable\n// parallel of syncShowTargets, declared on the root that OWNS the group:\n// { group: { count: [\"#id\", …], enable: { \"#id\": { any: … } } } }. The count\n// is this root's owned ticked boxes (a nested root's are its own, #15), the\n// enable folds its DNF payload with the same owned-field memo as every other\n// binding. Targets are id-only (warn-skip — the Ruby helper raised at\n// declare time) and resolved document-wide; a missing one is skipped.\nfunction syncGroupTargets(c, fieldValue) {\n for (const [group, targets] of Object.entries(parseShowTargets(c, \"group\", '\"ids[]\" => { ... }, \"tags[]\" => { ... }'))) {\n if (!targets || typeof targets !== \"object\" || Array.isArray(targets)) continue\n const text = String(fieldValue(group, true))\n for (const selector of Array.isArray(targets.count) ? targets.count : []) {\n if (!guardShowTargetSelector(selector, \"group\")) continue\n for (const node of document.querySelectorAll(selector)) if (node.textContent !== text) node.textContent = text\n }\n const enable = targets.enable && typeof targets.enable === \"object\" ? targets.enable : {}\n for (const [selector, payload] of Object.entries(enable)) {\n if (!guardShowTargetSelector(selector, \"group\")) continue\n const match = anyOfAllsMatches(payload?.any, fieldValue)\n if (match === null) {\n console.warn(`[phlex-reactive] malformed reactive_group_targets enable for ${selector} — skipped`)\n continue\n }\n for (const node of document.querySelectorAll(selector)) node.disabled = !match\n }\n }\n}\n\n// Boxes added or removed later — a stream append, a removal — fire no event,\n// so a root with a group binding (a header, a count, an enable, or a show,\n// show target or on_complete with a checked_* term) watches its subtree and\n// re-syncs when a mutation adds or removes a checkbox: the show pass, and the\n// on_complete latches re-armed without firing. Text writes (the count itself)\n// never qualify, so it cannot loop. Installed once — at connect, or by the\n// first morph that adds such a binding.\nfunction observeGroups(c) {\n if (c.groupObserver || typeof MutationObserver !== \"function\") return\n const onRoot = (name) => c.root.getAttribute?.(`data-reactive-${name}`)?.includes(\"checked_\")\n const targeted = !!c.root.getAttribute?.(\"data-reactive-group-targets\")\n if (!targeted && !c.root.querySelector?.(`${GROUP_BINDING_SELECTOR}, [data-reactive-show*=checked_]`) && !onRoot(\"show-targets\") && !onRoot(\"on-complete\")) return\n const boxIn = (node) => node.nodeType === 1 && (node.matches('input[type=\"checkbox\"]') || !!node.querySelector('input[type=\"checkbox\"]'))\n c.groupObserver = new MutationObserver((records) => {\n if (!records.some((r) => [...r.addedNodes, ...r.removedNodes].some(boxIn))) return\n c.show?.()\n c.arm?.()\n })\n c.groupObserver.observe(c.root, { childList: true, subtree: true })\n}\n\n// Whether this root declares an option filter (issue #163) — the connect()\n// gate. reactive_filter always emits input + option together, so requiring\n// BOTH also default-denies a half-built hand-authored binding.\nfunction filterEnabled(c) {\n return !!(\n c.root.getAttribute?.(\"data-reactive-filter-input\") &&\n c.root.getAttribute?.(\"data-reactive-filter-option\")\n )\n}\n\n// Whether a delegated input event came from the NAMED filter input (issue\n// #163). Anything else — another field's keystroke, a target without\n// matches() — skips the filter pass (the morph re-sync path bypasses this).\nfunction filterInputEvent(c, event) {\n const selector = c.root.getAttribute(\"data-reactive-filter-input\")\n return !!selector && typeof event.target?.matches === \"function\" && event.target.matches(selector)\n}\n\n// Re-apply the filter in one pass (issue #163): lowercase the named input's\n// current value, toggle `hidden` on every OWNED option by a substring match\n// against its haystack (data-reactive-filter-text, falling back to the\n// option's own text), collapse any group whose every contained option is\n// hidden, and reveal the empty target at 0 visible. A filtered-out option\n// also loses its listnav highlight so Enter can never pick an invisible row.\n// No owned input → leave visibility ALONE — a binding that can't resolve\n// must never break or blank the page (client-side default-deny). All\n// selectors resolve within this root, skipping nested reactive roots'\n// elements (issue #15 ownership; the predicate is hoisted once per pass).\nfunction syncFilter(c) {\n if (typeof c.root?.querySelectorAll !== \"function\") return\n const inputSelector = c.root.getAttribute(\"data-reactive-filter-input\")\n const optionSelector = c.root.getAttribute(\"data-reactive-filter-option\")\n if (!inputSelector || !optionSelector) return\n\n const owns = c.core.ownership()\n const input = [...c.root.querySelectorAll(inputSelector)].find(owns)\n if (!input) return\n\n const query = (input.value ?? \"\").trim().toLowerCase()\n let visible = 0\n for (const el of c.root.querySelectorAll(optionSelector)) {\n if (!owns(el)) continue // a nested root's option is its own controller's job\n const haystack = (el.getAttribute(\"data-reactive-filter-text\") ?? el.textContent ?? \"\").toLowerCase()\n // An option whose tag is already selected (reactive_tags, issue #203)\n // stays hidden through every re-filter — clearing the query must not\n // resurface an already-added tag.\n const hidden =\n el.hasAttribute?.(\"data-reactive-tags-selected\") || (query !== \"\" && !haystack.includes(query))\n el.hidden = hidden\n if (hidden) el.removeAttribute(\"data-reactive-highlighted\")\n else visible++\n }\n\n const groupSelector = c.root.getAttribute(\"data-reactive-filter-group\")\n if (groupSelector) {\n for (const group of c.root.querySelectorAll(groupSelector)) {\n if (!owns(group)) continue\n const contained = [...group.querySelectorAll(optionSelector)].filter(owns)\n // A group with no options isn't this filter's to decide — server state\n // stands (it may be a header the app toggles by other means).\n if (contained.length === 0) continue\n group.hidden = contained.every((el) => el.hidden)\n }\n }\n\n const emptySelector = c.root.getAttribute(\"data-reactive-filter-empty\")\n if (emptySelector) {\n for (const el of c.root.querySelectorAll(emptySelector)) {\n if (owns(el)) el.hidden = visible > 0\n }\n }\n}\n\n// Whether this root declares a tag-chip binding (issue #203) — the connect()\n// gate and every tags action's first check (an action bound without the root\n// binding is default-denied, the filter posture).\nfunction tagsEnabled(c) {\n return !!c.root.getAttribute?.(\"data-reactive-tags-field\")\n}\n\n// Whether this root owns at least one JSON-mode nested list (issue #208) —\n// the connect() gate. A cheap descendant probe: a form without one wires no\n// input/change listeners. Ownership is re-checked per sync, so a stray match\n// in a nested reactive root here is harmless (it just arms the listeners).\nfunction nestedJsonEnabled(c) {\n if (typeof c.root?.querySelector !== \"function\") return false\n return !!c.root.querySelector(\"[data-reactive-nested-json]\")\n}\n\n// The hidden input storing the comma-joined value, resolved fresh per use\n// (a morph replaces nodes — never cache it) and OWNED by this root (issue\n// #15). null when the selector resolves nothing — every caller then no-ops:\n// a binding that can't resolve must never break the page.\nfunction tagsField(c) {\n if (typeof c.root?.querySelectorAll !== \"function\") return null\n const selector = c.root.getAttribute(\"data-reactive-tags-field\")\n if (!selector) return null\n const owns = c.core.ownership()\n return [...c.root.querySelectorAll(selector)].find(owns) ?? null\n}\n\n// Parse the field's comma-joined value into the canonical tag list: split,\n// trim, drop blanks, dedupe case-insensitively KEEPING the first casing (the\n// server may have stored a ragged value — the projection normalizes without\n// rewriting the field, so we never fight server truth).\nfunction tagsRead(field) {\n const seen = new Set()\n const tags = []\n for (const part of String(field.value ?? \"\").split(\",\")) {\n const tag = part.trim()\n if (tag === \"\" || seen.has(tag.toLowerCase())) continue\n seen.add(tag.toLowerCase())\n tags.push(tag)\n }\n return tags\n}\n\n// Append any NEW tags (trimmed, non-blank, not already present under the\n// case-insensitive dedupe) and write the field once. Returns whether\n// anything was actually added — callers only clear the query input then.\nfunction tagsAddValues(c, values) {\n const field = tagsField(c)\n if (!field) return false\n\n const tags = tagsRead(field)\n const seen = new Set(tags.map((tag) => tag.toLowerCase()))\n let added = false\n for (const value of values) {\n const tag = String(value ?? \"\").trim()\n if (tag === \"\" || seen.has(tag.toLowerCase())) continue\n seen.add(tag.toLowerCase())\n tags.push(tag)\n added = true\n }\n if (added) tagsWrite(c, field, tags)\n return added\n}\n\n// The ONE writer: join, store, dispatch a real bubbling `input` on the field\n// (the set-value + dispatch contract, issue #183 — dirty tracking,\n// reactive_show, and compute all see the change), then re-project.\nfunction tagsWrite(c, field, tags) {\n field.value = tags.join(\",\")\n fireInput(field)\n syncTags(c)\n}\n\n// The query input the tags widget resets after a pick — the SAME input that\n// drives reactive_filter (a tags widget without filtering has none; the\n// caller then skips the reset).\nfunction tagsQueryInput(c) {\n if (typeof c.root?.querySelectorAll !== \"function\") return null\n const selector = c.root.getAttribute(\"data-reactive-filter-input\")\n if (!selector) return null\n const owns = c.core.ownership()\n return [...c.root.querySelectorAll(selector)].find(owns) ?? null\n}\n\n// Re-project the hidden field into the DOM (issue #203): rebuild the chip\n// list from the <template> and mark/hide the options whose tag is already\n// selected. The field is the single source of truth — this never writes it.\nfunction syncTags(c) {\n const field = tagsField(c)\n if (!field) return\n const tags = tagsRead(field)\n const owns = c.core.ownership()\n tagsRenderChips(c, tags, owns)\n tagsMarkOptions(c, tags, owns)\n}\n\n// Rebuild the chip list: clear the container and clone one chip per tag from\n// the server-owned template. The tag lands in the clone's\n// [data-reactive-tag-text] node via textContent (XSS-safe by construction —\n// never innerHTML, the reactive_text posture), and every tagsRemove trigger\n// in the clone gets the tag as its param. A missing list is a chip-less\n// widget (fine — the value still maintains); a missing/empty template warns\n// ONCE (a half-built binding should be loud, but never per-keystroke).\nfunction tagsRenderChips(c, tags, owns) {\n const list = [...c.root.querySelectorAll(\"[data-reactive-tags-list]\")].find(owns)\n if (!list) return\n\n const template = [...c.root.querySelectorAll(\"[data-reactive-tags-template]\")].find(owns)\n const chipProto = template?.content?.firstElementChild\n if (!chipProto) {\n if (!c.tagsWarnedTemplate) {\n console.warn(\n \"[phlex-reactive] reactive_tags: no chip <template data-reactive-tags-template> found in this root — \" +\n \"chips will not render (the hidden field still updates). Add a template with a \" +\n \"[data-reactive-tag-text] node and a reactive_tags_remove button.\"\n )\n c.tagsWarnedTemplate = true\n }\n return\n }\n\n while (list.firstChild) list.removeChild(list.firstChild)\n for (const tag of tags) {\n const chip = chipProto.cloneNode(true)\n chip.setAttribute?.(\"data-reactive-tag\", tag)\n const sink = chip.matches?.(\"[data-reactive-tag-text]\")\n ? chip\n : (chip.querySelectorAll?.(\"[data-reactive-tag-text]\") ?? [])[0]\n if (sink) sink.textContent = tag\n const removers = [...(chip.querySelectorAll?.('[data-action*=\"reactive#tagsRemove\"]') ?? [])]\n if (chip.matches?.('[data-action*=\"reactive#tagsRemove\"]')) removers.push(chip)\n for (const remover of removers) remover.setAttribute?.(\"data-reactive-tag-param\", tag)\n list.appendChild(chip)\n }\n}\n\n// Hide + mark every owned option whose DECLARED tag is already selected\n// (data-reactive-tags-selected — #syncFilter keeps it hidden through\n// re-filters), and resurface an option WE hid when its tag is removed. Only\n// marker-carrying options are un-hidden — an option hidden by the filter or\n// the server stays as-is. With a filter bound, one final #syncFilter re-folds\n// groups/empty against the new selected set.\nfunction tagsMarkOptions(c, tags, owns) {\n const selected = new Set(tags.map((tag) => tag.toLowerCase()))\n for (const el of c.root.querySelectorAll(\"[role=option]\")) {\n if (!owns(el)) continue\n const tag = el.getAttribute?.(\"data-reactive-tag-param\")\n if (!tag) continue\n if (selected.has(tag.toLowerCase())) {\n el.setAttribute(\"data-reactive-tags-selected\", \"true\")\n el.hidden = true\n el.removeAttribute?.(\"data-reactive-highlighted\")\n } else if (el.hasAttribute?.(\"data-reactive-tags-selected\")) {\n el.removeAttribute(\"data-reactive-tags-selected\")\n if (!filterEnabled(c)) el.hidden = false\n }\n }\n if (filterEnabled(c)) syncFilter(c)\n}\n\n// A fresh index per nested-row add (issue #208) — strictly monotonic and\n// clock-seeded, so it can never collide with server-rendered integer indexes\n// (0..n) NOR with a rapid same-millisecond double add.\nfunction nextNestedIndex(c) {\n c.nestedIndex = Math.max(c.nestedIndex + 1, Date.now())\n return c.nestedIndex\n}\n\n// Swap every NEW_ROW in the clone's name/id/for for the fresh index, so the\n// row posts as its own `…_attributes[<index>][field]` group and labels keep\n// pointing at their (renumbered) inputs.\nfunction renumberNestedRow(row, index) {\n const nodes = [row, ...(row.querySelectorAll?.(\"*\") ?? [])]\n for (const el of nodes) {\n for (const attr of [\"name\", \"id\", \"for\"]) {\n const value = el.getAttribute?.(attr)\n if (value && value.includes(\"NEW_ROW\")) el.setAttribute?.(attr, value.replaceAll(\"NEW_ROW\", String(index)))\n }\n }\n}\n\n// A half-built nested-rows binding should be loud, but never per-click.\nfunction warnNestedOnce(c, assoc) {\n if (c.nestedWarned) return\n console.warn(\n `[phlex-reactive] nested rows: no owned [data-reactive-nested-list=\"${assoc}\"] container + ` +\n `<template data-reactive-nested-template=\"${assoc}\"> pair found in this root — the add ` +\n \"trigger did nothing. Render both inside the same reactive root (reactive_nested_list / \" +\n \"reactive_nested_template).\"\n )\n c.nestedWarned = true\n}\n\n// Fill-then-add (issue #208): copy each source control's value into the\n// matching cloned-row field, keyed by the trailing bracket segment of the\n// field's name (#nestedJsonKey — the SAME inference JSON mode uses, so the\n// two features can't drift). Sources resolve root-scoped and owned (#15); an\n// unresolved source or an unmatched key is silently skipped (the row still\n// adds — a half-mapped binding must never throw on click). Returns the FIRST\n// source control read (for focus), or null. With `clear`, resets every source\n// it read via the set-value + dispatch contract (#183) so dirty/show/compute\n// observe the reset.\nfunction seedNestedRow(c, row, fromJson, clear) {\n if (!fromJson) return null\n let map\n try {\n map = JSON.parse(fromJson)\n } catch {\n return null\n }\n if (!map || typeof map !== \"object\") return null\n\n const owns = c.core.ownership()\n const rowFields = [...(row.querySelectorAll?.(\"input, select, textarea\") ?? [])]\n const sources = []\n for (const [key, selector] of Object.entries(map)) {\n const source = [...(c.root.querySelectorAll?.(selector) ?? [])].find(owns)\n if (!source) continue\n const target = rowFields.find((field) => nestedJsonKey(field.getAttribute?.(\"name\")) === key)\n if (!target) continue\n seedNestedField(target, source)\n sources.push(source)\n }\n if (clear) for (const source of sources) clearNestedSource(source)\n return sources[0] ?? null\n}\n\n// Copy a source control's value into a cloned-row field, then dispatch a\n// bubbling `input` (the set-value + dispatch contract, #183). Checkbox ↔\n// checkbox copies the checked state; every other target takes the source's\n// submit-shaped value (#nestedFieldValue), so a checkbox source feeding a\n// text field lands \"on\"/\"\" exactly as a submit would.\nfunction seedNestedField(target, source) {\n if (target.type === \"checkbox\") {\n target.checked = source.type === \"checkbox\" ? !!source.checked : nestedFieldValue(source) !== \"\"\n } else {\n target.value = nestedFieldValue(source)\n }\n fireInput(target)\n}\n\n// Reset a source control after a fill-then-add (issue #208), dispatching a\n// bubbling `input` so dirty tracking / reactive_show / compute see the reset.\nfunction clearNestedSource(source) {\n if (source.type === \"checkbox\") source.checked = false\n else source.value = \"\"\n fireInput(source)\n}\n\n// Resolve %{field} placeholders in a confirm message from a row's field map\n// (issue #222). Ruby-style %{name} tokens; an unresolved key is left verbatim\n// (a visible, debuggable placeholder — never an empty hole or a throw). A\n// message with no placeholders returns unchanged, so this is inert for every\n// server-rendered (already-interpolated) confirm string.\nfunction interpolateConfirm(message, fields) {\n if (!message.includes(\"%{\")) return message\n return message.replace(/%\\{(\\w+)\\}/g, (whole, key) =>\n Object.prototype.hasOwnProperty.call(fields, key) ? fields[key] : whole,\n )\n}\n\n// The remove itself, shared by the confirmed and no-confirm paths. Draft rows\n// leave the DOM; a persisted row (a hidden [_destroy] input present) is marked\n// \"1\" + hidden instead (set-value + dispatch contract, #183), so Rails destroys\n// it on save. Then re-sync every owned JSON-mode list (#208) — an absent row\n// IS the removal; a form without a JSON list iterates an empty set and exits.\nfunction removeNestedRow(c, row) {\n const destroy = [...(row.querySelectorAll?.('input[name$=\"[_destroy]\"]') ?? [])][0]\n if (destroy) {\n destroy.value = \"1\"\n fireInput(destroy)\n row.hidden = true\n } else {\n row.parentNode?.removeChild?.(row)\n }\n\n syncAllNestedJson(c)\n}\n\n// Serialize every owned JSON-mode list into its hidden field. The connect\n// seed and the input/remove re-syncs funnel through here so one place owns\n// the \"DOM rows → JSON field\" projection.\nfunction syncAllNestedJson(c) {\n if (typeof c.root?.querySelectorAll !== \"function\") return\n const owns = c.core.ownership()\n for (const list of [...c.root.querySelectorAll(\"[data-reactive-nested-json]\")].filter(owns)) {\n syncNestedJsonList(c, list.getAttribute(\"data-reactive-nested-json\"))\n }\n}\n\n// The hidden field a JSON-mode list mirrors into — resolved fresh (a morph\n// replaces nodes, never cache it) and OWNED by this root (#15). null when\n// the selector resolves nothing, so the caller no-ops (a half-built binding\n// must never break the page).\nfunction nestedJsonField(c, list) {\n const selector = list.getAttribute?.(\"data-reactive-nested-json-field\")\n if (!selector) return null\n const owns = c.core.ownership()\n return [...c.root.querySelectorAll(selector)].find(owns) ?? null\n}\n\n// One row → { key: value } over its named form controls. The JSON key is the\n// trailing bracket segment of each control's name (order[todos_attributes]\n// [3][title] → \"title\"; a bare `title` → \"title\"), the \"infer from input\n// names\" contract. The [_destroy] control is dropped (JSON has no destroy\n// marker). Later inputs with the same key win (last-wins, the DOM order).\n// Like a real submit (#299): an unchecked radio, a disabled control (its own\n// flag or a <fieldset disabled> ancestor: :disabled) and a nested reactive\n// root's control are skipped.\nfunction nestedRowObject(c, row) {\n const obj = {}\n const owns = c.core.ownership()\n for (const el of [...(row.querySelectorAll?.(\"input, select, textarea\") ?? [])]) {\n if (el.matches(\":disabled\") || (el.type === \"radio\" && !el.checked) || !owns(el)) continue\n const key = nestedJsonKey(el.getAttribute?.(\"name\"))\n if (key === null || key === \"_destroy\") continue\n obj[key] = nestedFieldValue(el)\n }\n return obj\n}\n\n// The trailing bracket segment of a field name (the inferred JSON key), or\n// the bare name when it carries no brackets. null for a nameless control\n// (a bare button, an unnamed helper input) — skipped by the caller.\nfunction nestedJsonKey(name) {\n if (!name) return null\n const match = name.match(/\\[([^\\][]+)\\]$/)\n return match ? match[1] : name\n}\n\n// A form control's submitted value: an unchecked checkbox contributes \"\" (it\n// wouldn't post at all), a checked one its value (default \"on\"); everything\n// else its .value. Keeps the JSON shape close to what a real form submit\n// would carry for the same control.\nfunction nestedFieldValue(el) {\n if (el.type === \"checkbox\") return el.checked ? (el.value || \"on\") : \"\"\n return el.value ?? \"\"\n}\n\n// Project ONE JSON-mode list's surviving rows into its hidden field. Each\n// row becomes an object keyed by the trailing bracket segment of its inputs'\n// names (…[title] → \"title\"); a hidden/_destroy-marked row is skipped (an\n// absent row IS the removal — JSON carries no destroy marker). The write\n// uses the set-value + dispatch contract (issue #183) so dirty tracking,\n// reactive_show, and compute see the change — but only when the value\n// actually changed, so a connect seed on an already-correct field is silent.\nfunction syncNestedJsonList(c, assoc) {\n const owns = c.core.ownership()\n const list = [...c.root.querySelectorAll(`[data-reactive-nested-list=\"${assoc}\"]`)].find(owns)\n if (!list) return\n const field = nestedJsonField(c, list)\n if (!field) return\n\n const rows = []\n for (const row of [...(list.querySelectorAll?.(\"[data-reactive-nested-row]\") ?? [])]) {\n if (!owns(row) || row.hidden) continue\n rows.push(nestedRowObject(c, row))\n }\n\n const next = JSON.stringify(rows)\n if (field.value === next) return\n field.value = next\n fireInput(field)\n}\n\n// Tag-chip input (issue #203) — the composed combobox/tags primitive. The\n// root's data-reactive-tags-field names the hidden input that stores the\n// COMMA-JOINED value; these three actions are its only writers. All of it is\n// FORM state (like text in an input) — no token, no POST: the surrounding\n// form submit carries the joined value. The chip list is re-projected from\n// the field on every write (#syncTags), so the field stays the single source\n// of truth.\n//\n// Enter on the query input: add the TYPED text — unless this Enter belongs\n// to listnav (reactive_tags_add composes after reactive_listnav on the same\n// keydown.enter). Two guards make the composition order-independent:\n// defaultPrevented means listnavPick ALREADY picked the highlighted option\n// (adding the typed text too would double-add); a still-visible highlighted\n// option means listnavPick is ABOUT to pick it (when tagsAdd is bound\n// first). Past the guards, Enter is OURS — preventDefault unconditionally so\n// it can never submit the enclosing form (blank input included). A\n// comma-separated paste splits into individual tags (the value is\n// comma-joined, so a comma can never be part of one tag). The input clears\n// only when something was actually added — a duplicate keeps the typed text\n// for correction.\nexport function tagsAdd(controller, core, event) {\n const c = ctx(controller, core)\n if (!tagsEnabled(c)) return\n if (event?.defaultPrevented) return\n if (c.core.listnavOptions(event).some((el) => el.hasAttribute?.(\"data-reactive-highlighted\"))) return\n event?.preventDefault?.()\n\n const input = event?.currentTarget ?? event?.target\n if (!input) return\n const added = tagsAddValues(c, String(input.value ?? \"\").split(\",\"))\n if (!added) return\n input.value = \"\"\n if (filterEnabled(c)) syncFilter(c)\n}\n\n// Click (or listnav Enter, which CLICKS the highlighted option) on a\n// preloaded option: add its DECLARED tag (data-reactive-tag-param — set by\n// reactive_tags_option, never free text). After a successful add, reset the\n// query so the next tag starts from the full list: clear the filter input,\n// re-narrow, and hand focus back for continued typing.\nexport function tagsPick(controller, core, event) {\n const c = ctx(controller, core)\n if (!tagsEnabled(c)) return\n event?.preventDefault?.()\n\n const trigger = event?.currentTarget ?? event?.target\n const tag = trigger?.getAttribute?.(\"data-reactive-tag-param\")\n if (!tag) return\n if (!tagsAddValues(c, [tag])) return\n\n const input = tagsQueryInput(c)\n if (!input) return\n input.value = \"\"\n syncFilter(c)\n input.focus?.()\n}\n\n// Click on a chip's remove button: drop its tag (case-insensitive match, the\n// dedupe convention) from the hidden value. The re-projection removes the\n// chip and resurfaces the option. Removing an absent tag is a no-op.\nexport function tagsRemove(controller, core, event) {\n const c = ctx(controller, core)\n if (!tagsEnabled(c)) return\n event?.preventDefault?.()\n\n const trigger = event?.currentTarget ?? event?.target\n const tag = trigger?.getAttribute?.(\"data-reactive-tag-param\")\n if (!tag) return\n const field = tagsField(c)\n if (!field) return\n\n const tags = tagsRead(field)\n const next = tags.filter((t) => t.toLowerCase() !== tag.toLowerCase())\n if (next.length === tags.length) return\n tagsWrite(c, field, next)\n}\n\n// Draft nested-attribute rows (issue #208) — the \"new parent + child rows\"\n// window. The rows are FORM state (the reactive_tags posture): no token, no\n// POST, ever — the surrounding REAL form submit carries Rails'\n// accepts_nested_attributes_for names and the server reconciles parent +\n// rows in ONE create. Add clones the association's server-owned\n// <template data-reactive-nested-template=\"assoc\"> row, swaps every NEW_ROW\n// in the clone's name/id/for for a fresh unique index (each row posts as its\n// own `…_attributes[<index>][field]` group), appends it to the owned\n// [data-reactive-nested-list=\"assoc\"] container, and focuses the new row's\n// first field. Several collections can share one root — everything is keyed\n// by the association name the trigger carries.\nexport function nestedAdd(controller, core, event) {\n const c = ctx(controller, core)\n event?.preventDefault?.()\n const trigger = event?.currentTarget ?? event?.target\n const assoc = trigger?.getAttribute?.(\"data-reactive-association-param\")\n if (!assoc) return\n if (typeof c.root?.querySelectorAll !== \"function\") return\n\n const owns = c.core.ownership()\n const list = [...c.root.querySelectorAll(`[data-reactive-nested-list=\"${assoc}\"]`)].find(owns)\n const template = [...c.root.querySelectorAll(`[data-reactive-nested-template=\"${assoc}\"]`)].find(owns)\n const proto = template?.content?.firstElementChild\n if (!list || !proto) {\n warnNestedOnce(c, assoc)\n return\n }\n\n const row = proto.cloneNode(true)\n renumberNestedRow(row, nextNestedIndex(c))\n list.appendChild(row)\n\n // Fill-then-add (issue #208 Scenario A): seed the cloned row from named\n // source controls OUTSIDE the row, then (optionally) clear the sources.\n // Runs AFTER renumber+append so a seeded field's name already carries its\n // final `[<index>][field]` form — the key match agrees with what JSON mode\n // reads. `seeded` is the FIRST source we cleared/read, so fill-then-add can\n // return focus to the sources instead of stealing it into the new row.\n const fromJson = trigger?.getAttribute?.(\"data-reactive-nested-from-param\")\n const clear = trigger?.getAttribute?.(\"data-reactive-nested-clear-param\") === \"true\"\n const firstSource = seedNestedRow(c, row, fromJson, clear)\n\n // Focus: inline-edit (no from:) focuses the new row's first field so you\n // type INTO it; fill-then-add keeps focus on the sources (the first one) so\n // you keep entering the next item — stealing focus would break that loop.\n if (fromJson) firstSource?.focus?.()\n else [...(row.querySelectorAll?.(\"input, select, textarea\") ?? [])][0]?.focus?.()\n\n // JSON mode (issue #208): a freshly-added row must land in the hidden field\n // immediately (seeded values included), so the serialized array reflects the\n // DOM even before the first keystroke. A no-op when not `as: :json`.\n if (list.getAttribute?.(\"data-reactive-nested-json\") === assoc) syncNestedJsonList(c, assoc)\n}\n\n// Remove the trigger's closest row wrapper. A DRAFT row (no [_destroy]\n// input) leaves the DOM — it was never persisted, so removing its fields IS\n// the removal. A PERSISTED row (an edit form rendered a hidden [_destroy]\n// input via nested_field_name) is marked \"1\" and hidden instead — Rails\n// destroys it on save. The mark dispatches a real bubbling `input` (the\n// set-value + dispatch contract, issue #183) so dirty tracking/compute see it.\nexport function nestedRemove(controller, core, event) {\n const c = ctx(controller, core)\n event?.preventDefault?.()\n const trigger = event?.currentTarget ?? event?.target\n const row = trigger?.closest?.(\"[data-reactive-nested-row]\")\n if (!row) return\n // The closest() walk must not escape this root — a root can itself sit\n // inside ANOTHER collection's row (the issue #15 closest-form posture).\n if (row.closest?.('[data-controller~=\"reactive\"]') !== c.root) return\n\n // Confirm gate (issue #218): reactive_nested_remove(confirm:) emits the SAME\n // data-reactive-confirm[-when]-param the other triggers do (nestedRemove reads\n // params via getAttribute, not event.params, so pull them off the trigger),\n // routed through the SAME #effectiveConfirmMessage + confirmResolver seam. A\n // static string always shows; a conditional Hash fires only when it matches,\n // else null. No confirm attr → null → the immediate-remove fast path.\n const confirm = trigger?.getAttribute?.(\"data-reactive-confirm-param\")\n const confirmWhen = trigger?.getAttribute?.(\"data-reactive-confirm-when-param\")\n const rawMessage = confirm || (confirmWhen ? conditionalConfirmMessage(c, confirmWhen) : null)\n if (!rawMessage) return removeNestedRow(c, row)\n\n // Per-row confirm interpolation (issue #222). A row added client-side is a\n // cloneNode of the <template>, and the clone carries the TEMPLATE's confirm\n // string verbatim — the renumber/seed steps never rewrite the confirm attr.\n // So resolve %{field} placeholders here, from THIS row's live field values\n // (read now, not at clone time, so a later edit is reflected). An unresolved\n // key is left as its literal %{key} (debuggable, never throws). Server-\n // rendered rows already interpolate server-side, so their finished strings\n // carry no %{}; this is a no-op for them.\n const fields = nestedRowObject(c, row)\n const message = interpolateConfirm(rawMessage, fields)\n\n // Gate through the overridable confirmResolver (issues #52/#55/#178) — a\n // themed dialog set with setConfirmResolver covers this trigger too. Pass the\n // row context (issue #222, superset of proposal 3) as an optional 2nd arg so\n // a power-user override can build the string itself; the message is already\n // interpolated for the default window.confirm path. Call the resolver INSIDE\n // the chain so even a SYNCHRONOUS override throw is a cancel (like a dismissed\n // dialog), and remove ONLY on a truthy resolution.\n return c.core.confirm(message, { el: trigger, row, fields }).then((ok) => {\n if (ok) removeNestedRow(c, row)\n })\n}\n\n// JSON-mode nested rows (issue #208) — the delegated input/change handler.\n// An app whose controller parses a serialized JSON param instead of Rails'\n// accepts_nested_attributes_for opts a list into `as: :json`; the client\n// then mirrors that list's rows into ONE hidden field as a JSON array on\n// every owned edit. Public so Stimulus can bind it; a no-op unless the\n// edited field belongs to a JSON-mode list this root owns.\nfunction syncNestedJson(c, event) {\n const target = event?.target\n if (!target || !c.core.owns(target)) return\n // Re-serialize every JSON-mode list (an edit could touch any of them; the\n // per-list owned-row scan is cheap and keeps this handler association-free).\n syncAllNestedJson(c)\n}\n\n// The numeric threshold keys (issue #176 part B) — the client half of the Ruby\n// SHOW_NUMERIC_KEYS. Order-independent; the evaluator reads the one that's\n// present. Each coerces BOTH sides to Number and compares.\nconst SHOW_NUMERIC_KEYS = [\"gte\", \"gt\", \"lte\", \"lt\"]\n\n// The length predicate keys (issue #226) — the client half of Ruby's\n// ShowConditions::LENGTH_KEYS. Length is counted in CODEPOINTS\n// ([...str].length), NOT UTF-16 code units (str.length), so Ruby's\n// String#length and this evaluator agree on multibyte values — the shared\n// fixture's emoji vector proves it.\nconst SHOW_LENGTH_KEYS = [\"len_eq\", \"len_gte\", \"len_gt\", \"len_lte\", \"len_lt\"]\n\n// The checked-count keys (issue #319) — the client half of Ruby's\n// ShowConditions::CHECKED_KEYS: how many owned boxes of a group are ticked.\nconst SHOW_CHECKED_KEYS = [\"checked_eq\", \"checked_gte\", \"checked_gt\", \"checked_lte\", \"checked_lt\"]\n\n// Evaluate one count predicate (len_* or checked_*). A count is a TOTAL\n// function (blank/absent → 0), so every field value is decidable — no\n// fail-closed special case like the numeric thresholds ({ length: 0 }\n// legitimately matches a blank field). A non-Integer LITERAL is a malformed\n// binding — warn-skip (null), default-deny.\nfunction countPredicateMatches(key, literal, count) {\n if (!Number.isInteger(literal)) {\n console.warn(`[phlex-reactive] reactive_show ${key}: needs an integer literal, got ${JSON.stringify(literal)} — skipped`)\n return null\n }\n switch (key.slice(key.indexOf(\"_\") + 1)) {\n case \"eq\":\n return count === literal\n case \"gte\":\n return count >= literal\n case \"gt\":\n return count > literal\n case \"lte\":\n return count <= literal\n case \"lt\":\n return count < literal\n default:\n return null\n }\n}\n\n// Evaluate one numeric threshold predicate against a field value. Returns\n// true/false for a decidable comparison, or null when the LITERAL itself is\n// non-numeric (a malformed binding — warn-skip, default-deny). A non-numeric\n// FIELD value (empty/blank/garbage) is treated as NaN → false: the\n// reveal-on-threshold notice stays hidden, the safe default. Shared by the\n// owned-binding evaluator (raw string literal off an attr) and the\n// cross-root/compound evaluator (a literal that arrived as a JSON number or\n// string).\nfunction numericPredicateMatches(key, literal, value) {\n const rhs = Number(literal)\n if (Number.isNaN(rhs)) {\n console.warn(`[phlex-reactive] reactive_show ${key}: needs a numeric literal, got ${JSON.stringify(literal)} — skipped`)\n return null\n }\n // A blank/whitespace field value must fail closed. Number(\"\") and\n // Number(\" \") are 0 (NOT NaN), so a bare Number()+isNaN check would wrongly\n // reveal a `lte:`/`lt:`/`gte: 0` binding on an EMPTY field. Force the\n // empty/blank case to NaN so the \"blank → hidden\" contract holds for every\n // operator, not just the ones where 0 happens to fail the comparison.\n const trimmed = value == null ? \"\" : String(value).trim()\n const n = trimmed === \"\" ? NaN : Number(trimmed)\n if (Number.isNaN(n)) return false\n switch (key) {\n case \"gte\":\n return n >= rhs\n case \"gt\":\n return n > rhs\n case \"lte\":\n return n <= rhs\n case \"lt\":\n return n < rhs\n default:\n return null\n }\n}\n\n// Evaluate an ALREADY-PARSED show predicate object (issue #164) — the\n// reactive_show_targets map embeds { equals/not/in } directly in its JSON, so\n// unlike showBindingMatches there are no attrs to read or re-parse. The same\n// literal-only vocabulary; anything else (empty, unknown keys, a non-array\n// in:) returns null and the caller warn-skips that target (default-deny — a\n// hand-built map entry must never flip visibility it doesn't declare).\nfunction showPredicateMatches(pred, value) {\n if (!pred || typeof pred !== \"object\") return null\n if (typeof pred.equals === \"string\") return value === pred.equals\n if (typeof pred.not === \"string\") return value !== pred.not\n if (Array.isArray(pred.in)) return pred.in.includes(value)\n // Numeric threshold predicates (issue #176 part B): the literal arrives as a\n // JSON number (or a numeric string) embedded in the predicate object — one\n // shared numericPredicateMatches with the owned-binding evaluator.\n for (const key of SHOW_NUMERIC_KEYS) {\n if (key in pred) return numericPredicateMatches(key, pred[key], value)\n }\n // Length predicates (issue #226): codepoint count vs an Integer literal.\n for (const key of SHOW_LENGTH_KEYS) {\n if (key in pred) return countPredicateMatches(key, pred[key], [...String(value ?? \"\")].length)\n }\n return null\n}\n\n// Evaluate one DNF TERM against a resolved field value (issue #180). A missing\n// field (null) or a malformed/unknown predicate folds to FALSE — fail-closed\n// (default-deny): a broken AND term can't pass, a broken OR term can't reveal.\nfunction dnfTermMatches(term, fieldValue) {\n if (!term || typeof term !== \"object\" || typeof term.field !== \"string\") return false\n // A checked_* term (issue #319) counts the group's ticked boxes: the\n // resolver's count form, or — from collected fields (a conditional confirm)\n // — the group's array of checked values (a lone box's true/false is 1/0).\n const checked = SHOW_CHECKED_KEYS.find((key) => key in term)\n if (checked) {\n const n = fieldValue(term.field, true)\n return countPredicateMatches(checked, term[checked], Array.isArray(n) ? n.length : Number(n) || 0) === true\n }\n // An absent owned field reads as \"\" — identical to the server evaluator\n // (ShowConditions.match? treats a missing field as blank). This keeps the\n // Ruby first-paint and the client live-toggle in exact agreement (the shared\n // fixture proves it). A malformed predicate still folds to false.\n const value = fieldValue(term.field) ?? \"\"\n return showPredicateMatches(term, value) === true\n}\n\n// Evaluate a DNF show payload (issue #180): { any: [group, …] } where each\n// GROUP is an array of terms (terms AND within a group, groups OR). Returns\n// true/false for a decidable payload, or null for a malformed one (no groups)\n// so the caller warn-skips and leaves visibility alone. This is the ONE shape\n// the 0.10 wire emits; showPayloadMatches routes the legacy shapes here or to\n// the compatibility arm below.\nfunction anyOfAllsMatches(groups, fieldValue) {\n if (!Array.isArray(groups) || groups.length === 0) return null\n // groups OR; within a group, terms AND (an empty group can't decide → false).\n return groups.some((group) => Array.isArray(group) && group.length > 0 &&\n group.every((term) => dnfTermMatches(term, fieldValue)))\n}\n\n// Every field a DNF payload's groups reference (issue #209) — drives the\n// \"leave the target alone when NO referenced field is owned\" skip, the\n// single-field-target skip generalized. Returns null for a malformed payload\n// (no groups, or no term names a field) so the caller warn-skips instead of\n// toggling on garbage (default-deny, like every other malformed-wire arm).\nfunction dnfGroupFields(groups) {\n if (!Array.isArray(groups) || groups.length === 0) return null\n const fields = new Set()\n for (const group of groups) {\n if (!Array.isArray(group)) continue\n for (const term of group) {\n if (term && typeof term === \"object\" && typeof term.field === \"string\") fields.add(term.field)\n }\n }\n return fields.size > 0 ? [...fields] : null\n}\n\n// Resolve the effective confirm message (issue #179). A plain string is the\n// static #52 form (always shown). A confirmWhen JSON payload is the CONDITIONAL\n// form — evaluated over the SAME collected fields reactive_compute reads — and\n// returns the message ONLY when it fires, else null (proceed, no dialog):\n// { groups, message } — the reactive_show conditions fold (anyOfAllsMatches)\n// { predicate, message } — a registered fn (setConfirmPredicate) over the fields\n// A missing predicate warns and returns null (PROCEED without a dialog) — the\n// compute unknown-reducer posture. This is soft-validation UX; the endpoint's\n// authorize/default-deny is the real gate, so failing OPEN here never grants\n// anything the server wouldn't already allow.\nfunction conditionalConfirmMessage(c, confirmWhen) {\n\n // Stimulus auto-parses a JSON-object -param value, so confirmWhen usually\n // arrives ALREADY parsed. Accept an object as-is; parse a string defensively\n // (a hand-built attr, or a non-Stimulus caller). A malformed string warns and\n // proceeds without a dialog (default-deny UX — the server is the real gate).\n let payload = confirmWhen\n if (typeof confirmWhen === \"string\") {\n try {\n payload = JSON.parse(confirmWhen)\n } catch {\n console.warn(`[phlex-reactive] malformed conditional confirm payload ${JSON.stringify(confirmWhen)} — skipped`)\n return null\n }\n }\n if (!payload || typeof payload !== \"object\") return null\n\n const { fields } = c.core.collectFields()\n const fieldValue = (name) => fields[name]\n\n let fires\n if (typeof payload.predicate === \"string\") {\n const fn = confirmPredicate(payload.predicate)\n if (!fn) {\n console.warn(`[phlex-reactive] confirm predicate \"${payload.predicate}\" is not registered — proceeding without a dialog (register it with setConfirmPredicate)`)\n return null\n }\n fires = !!fn(fields)\n } else {\n // Declarative: the DNF groups fold, identical to reactive_show — matches → fire.\n fires = anyOfAllsMatches(payload.groups?.any, fieldValue) === true\n }\n\n return fires ? payload.message : null\n}\n"
5
+ "// phlex/reactive/features/bindings — what a form root wires at connect and\n// re-syncs after a morph, all of it client-only and all of it reading the\n// root's own controls: show bindings and cross-root show targets (issues\n// #161, #164, #176, #180, #209), completion bindings (#226), option filtering\n// (#163), the tag-chip input (#203) and draft nested-attribute rows with their\n// JSON mode (#208). One of the feature modules the opt-in client imports on\n// demand (issue #275); in the default client it is part of the one file.\n//\n// Grouped because they share a shape — a marker check at connect, one\n// delegated listener pair on the root, a seed, the same pass again on\n// turbo:morph-element — and a page: a form with a show binding is the form\n// that filters options and adds rows. The seeds here write visible state\n// (and the JSON mode writes a hidden field a request collects), so with the\n// opt-in client the feature `gates`: a root's requests wait for it.\n//\n// The conditional confirm (issue #179) lives here too: its condition is the\n// same DNF fold a show binding uses, and its `predicate:` form reads the\n// confirm_predicate seam — imported here by its bare name, so the runtime no\n// longer imports it. A plain string confirm: never comes here; a conditional\n// one on the opt-in client waits for this module before its dialog (a root\n// with one preloads it).\n//\n// This module never imports the runtime: it reaches a controller through the\n// `core` handle.\nimport { confirmPredicate } from \"phlex/reactive/confirm_predicate\"\n\n// controller -> its context: the root, the core handle, and what used to be\n// the controller's own fields for these features. Every field it does not\n// set here starts undefined (falsy); a property name is one thing the\n// minifier never renames, so the record carries no more of them than it\n// must (issue #310).\nconst contexts = new WeakMap()\n// root element -> the same context, for the check_group op (issue #342),\n// which names a root, not a controller.\nconst rootContexts = new WeakMap()\n\nfunction ctx(controller, core) {\n let c = contexts.get(controller)\n if (!c) {\n contexts.set(controller, (c = { root: controller.element, core, nestedIndex: 0 }))\n if (c.root) rootContexts.set(c.root, c)\n }\n return c\n}\n\n// Add a delegated listener on the root for each of the space-separated\n// `types`, and keep its removal for disconnect().\nfunction listen(c, types, handler) {\n for (const type of types.split(\" \")) {\n c.root.addEventListener?.(type, handler)\n c.offs.push(() => c.root.removeEventListener?.(type, handler))\n }\n}\n\n// What connect() wires is one seed per binding: it runs now, again on\n// turbo:morph-element, and again from reseed().\nfunction seed(c, run) {\n listen(c, \"turbo:morph-element\", run)\n c.seeds.push(run)\n run()\n}\n\nexport function connect(controller, core) {\n const c = ctx(controller, core)\n c.offs = []\n c.seeds = []\n\n // Show bindings (issue #161) — ONLY when this root owns one, so a component\n // without any pays a single probe (the dirty-tracking gate precedent). ONE\n // delegated listener pair on the root (input + change bubble from every\n // owned field — no per-field wiring, and a reactive_compute output write\n // dispatches a real input event, so computed values drive visibility too).\n // The connect sync seeds the initial state — a plain replace re-connects —\n // and turbo:morph-element re-syncs after an in-place morph (which keeps the\n // element connected, fires no Stimulus lifecycle, and may preserve a\n // user-edited field value the server's hidden attrs don't reflect).\n if (showSyncEnabled(c)) {\n // A select-all header's edit flips its group first (issue #319) — on\n // `input`, which a click fires BEFORE `change`, or the pass below would\n // reset the header to its group's state first (the flip is idempotent, so\n // the `change` that follows flips nothing). The change events it\n // dispatches re-enter here and are skipped while it flips, so the pass\n // after the loop runs once, not once per box.\n c.show = (event) => {\n if (c.flipping) return\n // Only a user edit pushes the header onto its group; a morph (whose\n // turbo:morph-element bubbles from the header too) derives it instead.\n if (event?.type === \"input\" || event?.type === \"change\") flipGroup(c, event.target)\n // Whether this root has group bindings is read at connect and again on\n // a morph (which may add one, and so need the observer) — never per\n // keystroke.\n else {\n c.groups = hasGroups(c)\n observeGroups(c)\n }\n syncShow(c)\n }\n listen(c, \"input change\", c.show)\n seed(c, c.show)\n }\n\n // Completion bindings (issue #226) — ONLY when the root declares\n // data-reactive-on-complete (the show/filter gate precedent). ONE\n // delegated input+change listener evaluates every binding's DNF over the\n // owned fields and runs its ops on the RISING EDGE — the event-driven\n // flip to true. The connect pass ARMS without firing (a fresh render with\n // already-satisfied conditions must never self-fire — the $ops seed\n // precedent), and turbo:morph-element re-arms the same way after an\n // in-place morph. Listeners added HERE run after the Stimulus-wired\n // recompute delegation for the same event, so the evaluation reads\n // compute-NORMALIZED values (and a compute output write dispatches a real\n // input event that re-evaluates anyway).\n if (onCompleteEnabled(c)) {\n listen(c, \"input change\", (event) => syncOnComplete(c, event))\n seed(\n c,\n (c.arm = () => {\n syncOnComplete(c, null)\n observeGroups(c)\n }),\n )\n }\n\n // A form reset (issue #342) restores its fields AFTER the `reset` event\n // dispatches (and only if nothing cancelled it), so this waits a task, then\n // re-syncs the bindings and re-arms the completion latches without firing\n // (a reset fires no input/change, so a checked-count latch would stay high).\n if (c.show || c.arm) {\n listen(c, \"reset\", () =>\n setTimeout(() => {\n if (contexts.get(controller) !== c) return\n c.show?.()\n c.arm?.()\n }, 0),\n )\n }\n\n // Option filtering (issue #163) — ONLY when the root declares the binding\n // (reactive_filter emits both attrs together), so a component without one\n // pays two attribute reads. ONE delegated input listener on the root — the\n // handler re-filters only for events from the NAMED input, so keystrokes in\n // unrelated fields on a wide form never pay a filter pass. The connect sync\n // seeds from the input's current value (a plain replace re-connects; back\n // navigation may restore typed text), and turbo:morph-element re-applies\n // after an in-place morph (which keeps the element connected, fires no\n // Stimulus lifecycle, and may preserve the user's typed query while the\n // server re-rendered every option visible).\n if (filterEnabled(c)) {\n listen(c, \"input\", (event) => filterInputEvent(c, event) && syncFilter(c))\n seed(c, () => syncFilter(c))\n }\n\n // Tag-chip input (issue #203) — ONLY when the root names the hidden value\n // field (reactive_tags), so a component without one pays one attribute\n // read. The chip list is a CLIENT PROJECTION of the hidden field's\n // comma-joined value: connect seeds it (a plain replace re-connects with\n // the server-rendered value), and turbo:morph-element re-projects after an\n // in-place morph (the morph wrote server truth into the hidden field while\n // the chips DOM kept the pre-morph projection). Registered AFTER the\n // filter's listeners so a morph re-filters first and the tags pass then\n // re-marks selected options on the fresh visibility state.\n if (tagsEnabled(c)) seed(c, () => syncTags(c))\n\n // JSON-mode nested rows (issue #208) — ONLY when the root owns a list with\n // `as: :json`, so a form without one pays a single probe (the show/filter/\n // tags gate precedent). ONE delegated input + change listener re-serializes\n // the rows into the hidden field on every owned edit (nestedAdd/Remove call\n // the sync directly; this covers typing into a row's fields). The connect\n // seed writes the initial array (a plain replace re-connects), and\n // turbo:morph-element re-seeds after an in-place morph (which keeps the\n // element connected, fires no Stimulus lifecycle, and may have rewritten\n // the rows to server truth while the hidden field kept its pre-morph value).\n if (nestedJsonEnabled(c)) {\n listen(c, \"input change\", (event) => syncNestedJson(c, event))\n seed(c, () => syncAllNestedJson(c))\n }\n}\n\n// Remove every listener connect() added, so a stray event after a Turbo\n// morph/navigation never re-evaluates against a detached root.\nexport function disconnect(controller) {\n const c = contexts.get(controller)\n if (!c) return\n contexts.delete(controller)\n rootContexts.delete(c.root)\n for (const off of c.offs ?? []) off()\n c.groupObserver?.disconnect()\n}\n\n// Re-run the seeds that read field values, in connect() order — for a\n// feature that changed those values after connect() ran (the draft restore).\n// Each is the same re-sync a morph runs; on-complete re-ARMS without firing.\nexport function reseed(controller) {\n for (const run of contexts.get(controller)?.seeds ?? []) run()\n}\n\n// A value this module wrote is announced the way a user's edit is: a bubbling\n// `input` (the set-value + dispatch contract, issue #183), so dirty tracking,\n// reactive_show and compute all see it.\nfunction fireInput(el) {\n if (typeof el.dispatchEvent === \"function\") el.dispatchEvent(new Event(\"input\", { bubbles: true }))\n}\n\n// The Stimulus actions this module answers (the controller's own methods of\n// the same names hand the event here). tagsAdd, tagsPick, tagsRemove,\n// nestedAdd and nestedRemove take the controller themselves; the JSON sync is\n// also the root's own listener, so it keeps its context form inside.\nfunction publicSyncNestedJson(controller, core, event) {\n return syncNestedJson(ctx(controller, core), event)\n}\nexport { publicSyncNestedJson as syncNestedJson }\n\n// The runtime's call-in for a conditional confirm: the message when its\n// condition fires over this root's fields, else null.\nexport function confirmMessage(controller, core, confirmWhen) {\n return conditionalConfirmMessage(ctx(controller, core), confirmWhen)\n}\n\n// An id-only selector (the issue #159/#164 allowlist for anything that\n// escapes the root).\nconst ID_SELECTOR = /^#[A-Za-z_][\\w-]*$/\n\n// The selector matching every OWNED-element show binding: single-field\n// (data-reactive-show-field, issue #161) OR compound all:/any:\n// (data-reactive-show, issue #176). Both the connect() gate and the sync walk\n// use it so a compound-only root still enables the sync.\nconst SHOW_BINDING_SELECTOR = \"[data-reactive-show-field], [data-reactive-show]\"\n\n// Evaluate a show binding's declared literal predicate (issue #161) against\n// the controlling field's current value. Exactly one of the three predicate\n// attrs decides: equals (value === literal), not (value !== literal), in\n// (value ∈ a JSON string list). The vocabulary is fixed and literal-only —\n// never an expression, so there is no eval surface (the reactive_show helper\n// enforces the same shape loudly at render; this is the client half of the\n// two-sided posture). Returns true/false for a decidable binding, or null for\n// a malformed/missing predicate — the caller SKIPS a null so a hand-built or\n// stale binding never flips visibility it doesn't understand (default-deny,\n// like the op whitelist).\nfunction showBindingMatches(el, value) {\n const equals = el.getAttribute(\"data-reactive-show-equals\")\n if (equals !== null) return value === equals\n const not = el.getAttribute(\"data-reactive-show-not\")\n if (not !== null) return value !== not\n const inRaw = el.getAttribute(\"data-reactive-show-in\")\n if (inRaw !== null) {\n try {\n const list = JSON.parse(inRaw)\n if (Array.isArray(list)) return list.includes(value)\n } catch {\n // fall through to the warn below — malformed JSON and a non-array both skip\n }\n console.warn(`[phlex-reactive] malformed reactive_show in: list ${JSON.stringify(inRaw)} — skipped`)\n return null\n }\n // Numeric threshold predicates (issue #176 part B): gte/gt/lte/lt read the\n // literal off its own flat attr and compare Number(value) against it. Any\n // present numeric attr decides the binding — a non-numeric field value (NaN)\n // is false (hidden), and a non-numeric LITERAL warn-skips (null).\n for (const key of SHOW_NUMERIC_KEYS) {\n const raw = el.getAttribute(`data-reactive-show-${key}`)\n if (raw !== null) return numericPredicateMatches(key, raw, value)\n }\n console.warn(\"[phlex-reactive] a reactive_show binding declares no predicate — skipped\")\n return null\n}\n\n// Parse a compound show binding's JSON payload (issue #176 part A). Malformed\n// JSON degrades to null WITH a warn — a bad binding must never throw or blank\n// the page (client-side default-deny), but a collision (two bindings' JSON\n// mix-joined) is worth surfacing.\nfunction parseShowCompound(raw) {\n try {\n const parsed = JSON.parse(raw)\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) return parsed\n } catch {\n // fall through to the warn\n }\n console.warn(`[phlex-reactive] malformed compound reactive_show payload ${JSON.stringify(raw)} — skipped`)\n return null\n}\n\n// Parse a data-reactive-on-complete payload (issue #226): a JSON array of\n// { any: [[term, …], …], ops: [[op, args], …] } bindings. Malformed JSON, a\n// non-array, or a binding missing either half degrades to [] WITH a warn — a\n// bad payload must never throw or fire an op (client-side default-deny, the\n// parseShowCompound posture).\nfunction parseOnComplete(raw) {\n try {\n const list = JSON.parse(raw)\n if (\n Array.isArray(list) &&\n list.every((b) => b && typeof b === \"object\" && Array.isArray(b.any) && Array.isArray(b.ops))\n ) {\n return list\n }\n } catch {\n // fall through to the warn\n }\n console.warn(`[phlex-reactive] malformed reactive_on_complete payload ${JSON.stringify(raw)} — skipped`)\n return []\n}\n\n// Route a parsed data-reactive-show payload to the right evaluator. The 0.10\n// wire is { any: [ [term,…], … ] } (DNF — groups are ARRAYS). For a stale tab\n// still serving pre-0.10 HTML (deploy overlap), fall back to the 0.9.5 compound\n// shape { all: [term,…] } / { any: [term,…] } where the values are flat TERM\n// OBJECTS, not arrays. The nesting distinguishes them: DNF's any[0] is an Array.\n// DELETE the legacy arm in 0.11.\nfunction showPayloadMatches(payload, fieldValue) {\n if (!payload || typeof payload !== \"object\") return null\n const any = payload.any\n if (Array.isArray(any) && (any.length === 0 || Array.isArray(any[0]))) {\n return anyOfAllsMatches(any, fieldValue)\n }\n return legacyCompoundShowMatches(payload, fieldValue)\n}\n\n// LEGACY (0.9.5, deploy-overlap only — DELETE in 0.11): the flat all:/any:\n// compound fold, where terms are objects (not groups). Preserved so a morph of\n// stale pre-0.10 HTML doesn't go dead.\nfunction legacyCompoundShowMatches(payload, fieldValue) {\n const connective = Array.isArray(payload.all) ? \"all\" : Array.isArray(payload.any) ? \"any\" : null\n if (!connective) return null\n const terms = payload[connective]\n if (terms.length === 0) return null\n const results = terms.map((term) => dnfTermMatches(term, fieldValue))\n return connective === \"all\" ? results.every(Boolean) : results.some(Boolean)\n}\n\n// A cross-root show target must be a single ID selector (issue #164) — the\n// SAME shape the #159 mirror enforces (one shared regex), with its own warn so\n// a refused show target is distinguishable in the console. The client half of\n// the two-sided default-deny: reactive_show_targets raises at declare time; a\n// hand-built wire attr must not widen the escape to class/compound selectors.\n// A refused selector warns + skips — its siblings still apply. The group\n// targets (issue #343) share it, naming their own kind in the warn.\nfunction guardShowTargetSelector(selector, kind = \"show\") {\n if (typeof selector === \"string\" && ID_SELECTOR.test(selector)) return true\n console.warn(`[phlex-reactive] refused cross-root ${kind} target ${JSON.stringify(selector)} — skipped`)\n return false\n}\n\n\n// Whether this root owns a show binding (issue #161) or declares cross-root\n// show targets (issue #164) — the connect() gate, so a component with\n// neither pays only this probe (the #dirtyTrackingEnabled precedent). A\n// NESTED root's bindings don't count: its own controller instance syncs them\n// (issue #15 ownership). The targets attr is checked FIRST — one\n// getAttribute, cheaper than the binding walk.\nfunction showSyncEnabled(c) {\n if (c.root.getAttribute?.(\"data-reactive-show-targets\")) return true\n if (c.root.getAttribute?.(\"data-reactive-group-targets\")) return true\n // Single-field bindings carry -field; compound all:/any: bindings (issue\n // #176) carry data-reactive-show and have NO single controlling field, so\n // both selectors gate the sync.\n const nodes = c.root.querySelectorAll?.(SHOW_BINDING_SELECTOR) ?? []\n for (const el of nodes) if (c.core.owns(el)) return true\n for (const el of c.root.querySelectorAll?.(GROUP_BINDING_SELECTOR) ?? []) if (c.core.owns(el)) return true\n return false\n}\n\n// The connect() gate for completion bindings (issue #226) — one attribute read.\nfunction onCompleteEnabled(c) {\n return !!c.root.getAttribute?.(\"data-reactive-on-complete\")\n}\n\n// Parse-and-memoize the completion bindings, keyed on the RAW attr string:\n// a morph that rewrote the payload re-parses and RESETS the latches (the\n// morph listener's own arm pass then re-arms without firing). A removed\n// attr yields [] silently; malformed JSON warns (in parseOnComplete).\nfunction onCompleteBindings(c) {\n const raw = c.root.getAttribute?.(\"data-reactive-on-complete\") ?? null\n if (raw !== c.onCompleteRaw) {\n c.onCompleteRaw = raw\n c.onCompleteParsed = raw == null ? [] : parseOnComplete(raw)\n c.onCompleteStates = c.onCompleteParsed.map(() => false)\n }\n return c.onCompleteParsed\n}\n\n// Evaluate every completion binding (issue #226) over the owned fields —\n// the SAME memoized, scope-aware field resolver and DNF fold the show sync\n// uses — and run each binding's ops on ITS OWN rising edge. `event` null\n// (the connect/morph arm pass) updates the latches WITHOUT firing, so ops\n// only ever run from a real user gesture. An undecidable payload (no\n// groups) leaves its latch alone — default-deny, like every malformed-wire\n// arm. Ops resolve through runOps's root-scoped targets; a missing to:\n// defaults to \"@root\" (the $ops convention).\nfunction syncOnComplete(c, event) {\n const bindings = onCompleteBindings(c)\n if (!bindings.length) return\n\n const fieldValue = fieldResolver(c, c.core.ownership())\n bindings.forEach((binding, i) => {\n const matches = anyOfAllsMatches(binding.any, fieldValue)\n if (matches === null) return\n const fire = matches && !c.onCompleteStates[i] && Boolean(event)\n c.onCompleteStates[i] = matches\n if (fire) {\n c.core.applyOps(binding.ops, \"@root\")\n }\n })\n}\n\n// Re-evaluate every OWNED show binding in one pass (issue #161): read the\n// controlling field's current value, evaluate the declared literal predicate,\n// toggle `hidden`. A full pass (not per-target) for the same reason as\n// #scanDirty — a radio group's deselected radio fires no event — and because\n// several bindings can hang off one field (the value read is memoized per\n// pass). A binding whose field can't be resolved, or whose predicate is\n// malformed, leaves visibility ALONE — a bad binding must never break or\n// blank the page (client-side default-deny).\nfunction syncShow(c) {\n if (typeof c.root?.querySelectorAll !== \"function\") return\n\n const owns = c.core.ownership()\n const scope = c.root.getAttribute?.(\"data-reactive-scope\") || null\n // A memoized resolver shared by every binding in this pass — a field driving\n // several bindings (and several DNF terms) reads exactly once. Scope-aware:\n // a bare field `director` resolves as `[name=\"scope[director]\"]` (issue #180).\n const fieldValue = fieldResolver(c, owns)\n for (const el of c.root.querySelectorAll(SHOW_BINDING_SELECTOR)) {\n if (!owns(el)) continue // a nested root's binding is its own controller's job\n\n // The 0.10 DNF payload (issue #180): data-reactive-show carries\n // { any: [ [term,…], … ] }. The legacy flat-attr and 0.9.5-compound read\n // arms live in showPayloadMatches/showBindingMatches for deploy overlap.\n const payloadRaw = el.getAttribute(\"data-reactive-show\")\n if (payloadRaw !== null) {\n const match = showPayloadMatches(parseShowCompound(payloadRaw), fieldValue)\n if (match !== null) applyShowVisibility(el, match, owns, scope)\n continue\n }\n\n // LEGACY flat-attr binding (pre-0.10, deploy overlap — removed in 0.11).\n const name = el.getAttribute(\"data-reactive-show-field\")\n if (!name) continue\n const value = fieldValue(name)\n if (value === null) continue // no owned field with that name — leave it be\n const match = showBindingMatches(el, value)\n if (match === null) continue // malformed predicate — warned + skipped\n applyShowVisibility(el, match, owns, scope)\n }\n\n // The group bindings (issue #319) and the cross-root pass (issue #164)\n // share the same owned-field memo, so a field driving several reads once.\n if (c.groups) syncGroups(c, fieldValue, owns, scope)\n syncShowTargets(c, fieldValue)\n syncGroupTargets(c, fieldValue)\n}\n\n// Toggle `hidden` (and, when the binding declares data-reactive-show-disable,\n// the `disabled` of every owned named control inside it) from a match result\n// (issue #180). Disabling a hidden section's controls stops them submitting —\n// the stale-value fix. A visible section re-enables them. Controls a nested\n// reactive root owns are left alone (#15 ownership).\nfunction applyShowVisibility(el, match, owns, scope) {\n el.hidden = !match\n if (el.getAttribute(\"data-reactive-show-disable\") !== \"true\") return\n if (typeof el.querySelectorAll !== \"function\") return\n for (const control of el.querySelectorAll(\"input[name], select[name], textarea[name]\")) {\n if (owns(control)) control.disabled = !match\n }\n // The element itself may be a named control (a bare field with a binding).\n if (el.name && owns(el)) el.disabled = !match\n}\n\n// Apply the declared cross-root show targets (issue #164) — the visibility\n// parallel of #applyComputeMirrors. For each declared field: read the OWNED\n// field's current value (never a nested root's — you can only drive outside\n// visibility from a field this root owns), then for each \"#id\" → predicate\n// entry: guard the selector id-only (warn-and-skip; the Ruby helper raised\n// at declare time — two-sided default-deny), resolve it DOCUMENT-WIDE, and\n// toggle `hidden`. A target id not on the page is silently skipped (an\n// unrendered tab pane is normal); a malformed predicate warn-skips its one\n// target while siblings still apply. With no map declared this is one\n// getAttribute and out.\n//\n// A \"#id\" KEY (issue #209) is a TARGET-KEYED entry instead: its value is the\n// same DNF payload data-reactive-show holds, folded with per-term owned-field\n// reads — the multi-field cross-root case. The \"#\" prefix routes unambiguously\n// (a field name may never start with \"#\"; the Ruby helper raises).\nfunction syncShowTargets(c, fieldValue) {\n const map = parseShowTargets(c)\n for (const [name, targets] of Object.entries(map)) {\n if (name.startsWith(\"#\")) {\n applyConditionsTarget(name, targets, fieldValue)\n continue\n }\n if (!targets || typeof targets !== \"object\" || Array.isArray(targets)) continue\n const value = fieldValue(name)\n if (value === null) continue // no owned field with that name — leave them be\n // Every target's terms share this one field, so a constant resolver folds\n // the group (issue #180): a target's value is a DNF GROUP (terms ANDed).\n // (A checked_* term asks for the group's count instead, issue #319.)\n const resolve = (_, count) => (count ? fieldValue(name, true) : value)\n for (const [selector, group] of Object.entries(targets)) {\n if (!guardShowTargetSelector(selector)) continue\n // 0.10 wire: the value is a DNF GROUP (an array of terms, ANDed).\n // LEGACY (0.9.5, deploy overlap — DELETE in 0.11): a flat predicate\n // OBJECT ({ equals/not/in/gte… }) routed through showPredicateMatches.\n let match\n if (Array.isArray(group)) {\n if (group.length === 0) {\n console.warn(`[phlex-reactive] malformed reactive_show_targets group for ${selector} — skipped`)\n continue\n }\n match = group.every((term) => dnfTermMatches(term, resolve))\n } else {\n const legacy = showPredicateMatches(group, value)\n if (legacy === null) {\n console.warn(`[phlex-reactive] malformed reactive_show_targets predicate for ${selector} — skipped`)\n continue\n }\n match = legacy\n }\n for (const node of document.querySelectorAll(selector)) node.hidden = !match\n }\n }\n}\n\n// Apply ONE target-keyed conditions entry (issue #209): \"#id\" → the DNF\n// payload { any: [[term,…],…] }, folded by the SAME anyOfAllsMatches as an\n// in-root reactive_show — each term reads its OWN owned field, a missing\n// owned field reads as blank (fail-closed, the shared-fixture contract). A\n// target whose referenced fields are ALL unowned is left alone — the\n// single-field skip generalized (this root has nothing to evaluate with) —\n// unless every term is a checked_* count (issue #348): a group with no owned\n// boxes counts 0, so that target is always decidable, as in-root. A\n// malformed payload warn-skips its one target while siblings still apply;\n// the selector guard is the same id-only allowlist as every cross-root arm.\nfunction applyConditionsTarget(selector, payload, fieldValue) {\n if (!guardShowTargetSelector(selector)) return\n const groups = payload && typeof payload === \"object\" && !Array.isArray(payload) ? payload.any : null\n const fields = dnfGroupFields(groups)\n if (fields === null) {\n console.warn(`[phlex-reactive] malformed reactive_show_targets conditions for ${selector} — skipped`)\n return\n }\n if (fields.every((name) => fieldValue(name) === null) && !countsOnly(groups)) return // no owned field — leave it be\n const match = anyOfAllsMatches(groups, fieldValue)\n if (match === null) return // unreachable after dnfGroupFields, kept fail-closed\n for (const node of document.querySelectorAll(selector)) node.hidden = !match\n}\n\n// The declared cross-root show-target map (issue #164): a JSON object of\n// { field: { \"#id\": predicate } } from data-reactive-show-targets (emitted\n// by reactive_show_targets on the root). Absent degrades to {}; malformed\n// degrades to {} WITH a warn — never a throw (the #parseComputeMirror\n// contract), but never silent either: the likeliest cause is TWO\n// reactive_show_targets calls on one root, whose JSON strings Phlex `mix`\n// space-joined into an unparseable attr. The warn names the fix.\nfunction parseShowTargets(c, kind = \"show\", fix = \"mode: { ... }, kind: { ... }\") {\n const raw = c.root.getAttribute?.(`data-reactive-${kind}-targets`)\n if (!raw) return {}\n try {\n const parsed = JSON.parse(raw)\n if (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) return parsed\n } catch {\n // fall through to the shared warn below\n }\n console.warn(\n `[phlex-reactive] malformed data-reactive-${kind}-targets — ignored. ` +\n `Did two reactive_${kind}_targets calls collide on one root? Declare every entry in ONE call: ` +\n `reactive_${kind}_targets(${fix})`\n )\n return {}\n}\n\n// The current value of the OWNED field controlling a show binding, as the\n// string the literal predicate compares against. Mirrors #collectFields'\n// per-kind reads: a checkbox reports its checked state (\"true\"/\"false\" — its\n// .value is the constant \"on\", and the checkbox wins over the hidden input\n// Rails pairs with it); a radio group reports the CHECKED radio's value (\"\"\n// when none is); anything else reports .value first-wins. Returns null when\n// no owned field carries the name — the caller then leaves visibility alone.\nfunction showFieldValue(c, name, owns, scope) {\n let sawRadio = false\n let first = null\n for (const el of c.root.querySelectorAll(namedSelector(name, scope))) {\n if (!owns(el)) continue\n if (el.type === \"checkbox\") return el.checked ? \"true\" : \"false\"\n if (el.type === \"radio\") {\n if (el.checked) return el.value ?? \"\"\n sawRadio = true\n continue\n }\n first ??= el\n }\n if (first) return first.value ?? \"\"\n return sawRadio ? \"\" : null\n}\n\n// Scope (issue #180): a bare field `director` under `data-reactive-scope=\n// \"form\"` resolves as `[name=\"form[director]\"]`. A name already carrying a\n// bracket (a raw wire name the author passed, like \"ids[]\") is used verbatim.\nfunction namedSelector(name, scope) {\n return `[name=\"${scope && !name.includes(\"[\") ? `${scope}[${name}]` : name}\"]`\n}\n\n// The per-pass field resolver every DNF fold reads (show, enable, on-complete).\n// fieldValue(name) is the field's string value; fieldValue(name, true) is how\n// many owned boxes of that checkbox group are ticked (the checked_* terms,\n// issue #319). Memoized, so a field driving several bindings reads once.\nfunction fieldResolver(c, owns) {\n const scope = c.root.getAttribute?.(\"data-reactive-scope\") || null\n const values = new Map()\n return (name, count) => {\n const key = count ? `#${name}` : name\n if (!values.has(key)) {\n values.set(key, count ? groupBoxes(c, name, owns, scope).filter((box) => box.checked).length : showFieldValue(c, name, owns, scope))\n }\n return values.get(key)\n }\n}\n\n// --- Bulk selection (issue #319) ---------------------------------------------\n// A checkbox GROUP is the owned boxes sharing one name. Three bindings read it:\n// the select-all header (data-reactive-select-all), the ticked count\n// (data-reactive-count), and the checked_* terms any condition can use —\n// reactive_enable (data-reactive-enable) flips the element's own `disabled`\n// from one. They re-sync with the show pass: on every input/change, on a\n// morph, and when boxes are added or removed (observeGroups).\nconst GROUP_BINDING_SELECTOR = \"[data-reactive-enable], [data-reactive-select-all], [data-reactive-count]\"\n\n// Whether the root holds any group binding (owned or not — a stray nested\n// one only costs the walk, which still skips it).\nfunction hasGroups(c) {\n return !!c.root.querySelector?.(GROUP_BINDING_SELECTOR)\n}\n\n// The owned checkboxes of a group (a nested root's boxes are its own, #15).\nfunction groupBoxes(c, name, owns, scope) {\n return [...c.root.querySelectorAll(namedSelector(name, scope))].filter((el) => el.type === \"checkbox\" && owns(el))\n}\n\n// A header's edit ticks or unticks every owned box of its group (the header\n// itself is never a member). The show listener syncs once afterwards.\nfunction flipGroup(c, header) {\n const group = header?.getAttribute?.(\"data-reactive-select-all\")\n if (group && c.core.owns(header)) setGroup(c, group, header.checked, header)\n}\n\n// The check_group op (issue #342): set a root's group the way its header\n// would, then sync its bindings once. A root this module never connected (no\n// group binding, only the boxes) still flips, with the #15 ownership check.\nexport function checkGroup(root, group, checked) {\n if (!group) return\n const c = rootContexts.get(root) ?? {\n root,\n core: { ownership: () => (el) => el.closest('[data-controller~=\"reactive\"]') === root },\n }\n setGroup(c, group, checked !== false)\n c.show?.()\n}\n\n// Tick or untick every owned box of a group, and dispatch `input` + `change`\n// on each one it flips so computes, shows and on-complete bindings see a real\n// edit. The re-entrant events are skipped by the show listener while this\n// runs (c.flipping); the caller syncs once afterwards.\nfunction setGroup(c, group, checked, except) {\n const scope = c.root.getAttribute?.(\"data-reactive-scope\") || null\n const flipped = groupBoxes(c, group, c.core.ownership(), scope).filter((box) => box !== except && box.checked !== checked)\n // Every box first, then the events: a listener (a checked-count\n // on_complete) must see the group's final count, never a half-flipped one.\n for (const box of flipped) box.checked = checked\n c.flipping = true\n try {\n for (const box of flipped) {\n // input then change, as a click does: computes listen on input.\n for (const type of [\"input\", \"change\"]) box.dispatchEvent?.(new Event(type, { bubbles: true }))\n }\n } finally {\n c.flipping = false\n }\n}\n\n// One pass over the owned group bindings: enable from its conditions, the\n// count as text (change-guarded, like #mirrorText), and each header's\n// checked/indeterminate from its group (the header itself never counts).\nfunction syncGroups(c, fieldValue, owns, scope) {\n for (const el of c.root.querySelectorAll(GROUP_BINDING_SELECTOR)) {\n if (!owns(el)) continue\n const enable = el.getAttribute(\"data-reactive-enable\")\n if (enable !== null) {\n const match = showPayloadMatches(parseShowCompound(enable), fieldValue)\n if (match !== null) el.disabled = !match\n }\n const counted = el.getAttribute(\"data-reactive-count\")\n if (counted) {\n const text = String(fieldValue(counted, true))\n if (el.textContent !== text) el.textContent = text\n }\n const group = el.getAttribute(\"data-reactive-select-all\")\n if (group) {\n const boxes = groupBoxes(c, group, owns, scope).filter((box) => box !== el)\n const ticked = boxes.filter((box) => box.checked).length\n el.checked = ticked > 0 && ticked === boxes.length\n el.indeterminate = ticked > 0 && ticked < boxes.length\n }\n }\n}\n\n// The declared cross-root group targets (issue #343) — the count and enable\n// parallel of syncShowTargets, declared on the root that OWNS the group:\n// { group: { count: [\"#id\", …], enable: { \"#id\": { any: … } } } }. The count\n// is this root's owned ticked boxes (a nested root's are its own, #15), the\n// enable folds its DNF payload with the same owned-field memo as every other\n// binding. Targets are id-only (warn-skip — the Ruby helper raised at\n// declare time) and resolved document-wide; a missing one is skipped.\nfunction syncGroupTargets(c, fieldValue) {\n for (const [group, targets] of Object.entries(parseShowTargets(c, \"group\", '\"ids[]\" => { ... }, \"tags[]\" => { ... }'))) {\n if (!targets || typeof targets !== \"object\" || Array.isArray(targets)) continue\n const text = String(fieldValue(group, true))\n for (const selector of Array.isArray(targets.count) ? targets.count : []) {\n if (!guardShowTargetSelector(selector, \"group\")) continue\n for (const node of document.querySelectorAll(selector)) if (node.textContent !== text) node.textContent = text\n }\n const enable = targets.enable && typeof targets.enable === \"object\" ? targets.enable : {}\n for (const [selector, payload] of Object.entries(enable)) {\n if (!guardShowTargetSelector(selector, \"group\")) continue\n const match = anyOfAllsMatches(payload?.any, fieldValue)\n if (match === null) {\n console.warn(`[phlex-reactive] malformed reactive_group_targets enable for ${selector} — skipped`)\n continue\n }\n for (const node of document.querySelectorAll(selector)) node.disabled = !match\n }\n }\n}\n\n// Boxes added or removed later — a stream append, a removal — fire no event,\n// so a root with a group binding (a header, a count, an enable, or a show,\n// show target or on_complete with a checked_* term) watches its subtree and\n// re-syncs when a mutation adds or removes a checkbox: the show pass, and the\n// on_complete latches re-armed without firing. Text writes (the count itself)\n// never qualify, so it cannot loop. Installed once — at connect, or by the\n// first morph that adds such a binding.\nfunction observeGroups(c) {\n if (c.groupObserver || typeof MutationObserver !== \"function\") return\n const onRoot = (name) => c.root.getAttribute?.(`data-reactive-${name}`)?.includes(\"checked_\")\n const targeted = !!c.root.getAttribute?.(\"data-reactive-group-targets\")\n if (!targeted && !c.root.querySelector?.(`${GROUP_BINDING_SELECTOR}, [data-reactive-show*=checked_]`) && !onRoot(\"show-targets\") && !onRoot(\"on-complete\")) return\n const boxIn = (node) => node.nodeType === 1 && (node.matches('input[type=\"checkbox\"]') || !!node.querySelector('input[type=\"checkbox\"]'))\n c.groupObserver = new MutationObserver((records) => {\n if (!records.some((r) => [...r.addedNodes, ...r.removedNodes].some(boxIn))) return\n c.show?.()\n c.arm?.()\n })\n c.groupObserver.observe(c.root, { childList: true, subtree: true })\n}\n\n// Whether this root declares an option filter (issue #163) — the connect()\n// gate. reactive_filter always emits input + option together, so requiring\n// BOTH also default-denies a half-built hand-authored binding.\nfunction filterEnabled(c) {\n return !!(\n c.root.getAttribute?.(\"data-reactive-filter-input\") &&\n c.root.getAttribute?.(\"data-reactive-filter-option\")\n )\n}\n\n// Whether a delegated input event came from the NAMED filter input (issue\n// #163). Anything else — another field's keystroke, a target without\n// matches() — skips the filter pass (the morph re-sync path bypasses this).\nfunction filterInputEvent(c, event) {\n const selector = c.root.getAttribute(\"data-reactive-filter-input\")\n return !!selector && typeof event.target?.matches === \"function\" && event.target.matches(selector)\n}\n\n// Re-apply the filter in one pass (issue #163): lowercase the named input's\n// current value, toggle `hidden` on every OWNED option by a substring match\n// against its haystack (data-reactive-filter-text, falling back to the\n// option's own text), collapse any group whose every contained option is\n// hidden, and reveal the empty target at 0 visible. A filtered-out option\n// also loses its listnav highlight so Enter can never pick an invisible row.\n// No owned input → leave visibility ALONE — a binding that can't resolve\n// must never break or blank the page (client-side default-deny). All\n// selectors resolve within this root, skipping nested reactive roots'\n// elements (issue #15 ownership; the predicate is hoisted once per pass).\nfunction syncFilter(c) {\n if (typeof c.root?.querySelectorAll !== \"function\") return\n const inputSelector = c.root.getAttribute(\"data-reactive-filter-input\")\n const optionSelector = c.root.getAttribute(\"data-reactive-filter-option\")\n if (!inputSelector || !optionSelector) return\n\n const owns = c.core.ownership()\n const input = [...c.root.querySelectorAll(inputSelector)].find(owns)\n if (!input) return\n\n const query = (input.value ?? \"\").trim().toLowerCase()\n let visible = 0\n for (const el of c.root.querySelectorAll(optionSelector)) {\n if (!owns(el)) continue // a nested root's option is its own controller's job\n const haystack = (el.getAttribute(\"data-reactive-filter-text\") ?? el.textContent ?? \"\").toLowerCase()\n // An option whose tag is already selected (reactive_tags, issue #203)\n // stays hidden through every re-filter — clearing the query must not\n // resurface an already-added tag.\n const hidden =\n el.hasAttribute?.(\"data-reactive-tags-selected\") || (query !== \"\" && !haystack.includes(query))\n el.hidden = hidden\n if (hidden) el.removeAttribute(\"data-reactive-highlighted\")\n else visible++\n }\n\n const groupSelector = c.root.getAttribute(\"data-reactive-filter-group\")\n if (groupSelector) {\n for (const group of c.root.querySelectorAll(groupSelector)) {\n if (!owns(group)) continue\n const contained = [...group.querySelectorAll(optionSelector)].filter(owns)\n // A group with no options isn't this filter's to decide — server state\n // stands (it may be a header the app toggles by other means).\n if (contained.length === 0) continue\n group.hidden = contained.every((el) => el.hidden)\n }\n }\n\n const emptySelector = c.root.getAttribute(\"data-reactive-filter-empty\")\n if (emptySelector) {\n for (const el of c.root.querySelectorAll(emptySelector)) {\n if (owns(el)) el.hidden = visible > 0\n }\n }\n}\n\n// Whether this root declares a tag-chip binding (issue #203) — the connect()\n// gate and every tags action's first check (an action bound without the root\n// binding is default-denied, the filter posture).\nfunction tagsEnabled(c) {\n return !!c.root.getAttribute?.(\"data-reactive-tags-field\")\n}\n\n// Whether this root owns at least one JSON-mode nested list (issue #208) —\n// the connect() gate. A cheap descendant probe: a form without one wires no\n// input/change listeners. Ownership is re-checked per sync, so a stray match\n// in a nested reactive root here is harmless (it just arms the listeners).\nfunction nestedJsonEnabled(c) {\n if (typeof c.root?.querySelector !== \"function\") return false\n return !!c.root.querySelector(\"[data-reactive-nested-json]\")\n}\n\n// The hidden input storing the comma-joined value, resolved fresh per use\n// (a morph replaces nodes — never cache it) and OWNED by this root (issue\n// #15). null when the selector resolves nothing — every caller then no-ops:\n// a binding that can't resolve must never break the page.\nfunction tagsField(c) {\n if (typeof c.root?.querySelectorAll !== \"function\") return null\n const selector = c.root.getAttribute(\"data-reactive-tags-field\")\n if (!selector) return null\n const owns = c.core.ownership()\n return [...c.root.querySelectorAll(selector)].find(owns) ?? null\n}\n\n// Parse the field's comma-joined value into the canonical tag list: split,\n// trim, drop blanks, dedupe case-insensitively KEEPING the first casing (the\n// server may have stored a ragged value — the projection normalizes without\n// rewriting the field, so we never fight server truth).\nfunction tagsRead(field) {\n const seen = new Set()\n const tags = []\n for (const part of String(field.value ?? \"\").split(\",\")) {\n const tag = part.trim()\n if (tag === \"\" || seen.has(tag.toLowerCase())) continue\n seen.add(tag.toLowerCase())\n tags.push(tag)\n }\n return tags\n}\n\n// Append any NEW tags (trimmed, non-blank, not already present under the\n// case-insensitive dedupe) and write the field once. Returns whether\n// anything was actually added — callers only clear the query input then.\nfunction tagsAddValues(c, values) {\n const field = tagsField(c)\n if (!field) return false\n\n const tags = tagsRead(field)\n const seen = new Set(tags.map((tag) => tag.toLowerCase()))\n let added = false\n for (const value of values) {\n const tag = String(value ?? \"\").trim()\n if (tag === \"\" || seen.has(tag.toLowerCase())) continue\n seen.add(tag.toLowerCase())\n tags.push(tag)\n added = true\n }\n if (added) tagsWrite(c, field, tags)\n return added\n}\n\n// The ONE writer: join, store, dispatch a real bubbling `input` on the field\n// (the set-value + dispatch contract, issue #183 — dirty tracking,\n// reactive_show, and compute all see the change), then re-project.\nfunction tagsWrite(c, field, tags) {\n field.value = tags.join(\",\")\n fireInput(field)\n syncTags(c)\n}\n\n// The query input the tags widget resets after a pick — the SAME input that\n// drives reactive_filter (a tags widget without filtering has none; the\n// caller then skips the reset).\nfunction tagsQueryInput(c) {\n if (typeof c.root?.querySelectorAll !== \"function\") return null\n const selector = c.root.getAttribute(\"data-reactive-filter-input\")\n if (!selector) return null\n const owns = c.core.ownership()\n return [...c.root.querySelectorAll(selector)].find(owns) ?? null\n}\n\n// Re-project the hidden field into the DOM (issue #203): rebuild the chip\n// list from the <template> and mark/hide the options whose tag is already\n// selected. The field is the single source of truth — this never writes it.\nfunction syncTags(c) {\n const field = tagsField(c)\n if (!field) return\n const tags = tagsRead(field)\n const owns = c.core.ownership()\n tagsRenderChips(c, tags, owns)\n tagsMarkOptions(c, tags, owns)\n}\n\n// Rebuild the chip list: clear the container and clone one chip per tag from\n// the server-owned template. The tag lands in the clone's\n// [data-reactive-tag-text] node via textContent (XSS-safe by construction —\n// never innerHTML, the reactive_text posture), and every tagsRemove trigger\n// in the clone gets the tag as its param. A missing list is a chip-less\n// widget (fine — the value still maintains); a missing/empty template warns\n// ONCE (a half-built binding should be loud, but never per-keystroke).\nfunction tagsRenderChips(c, tags, owns) {\n const list = [...c.root.querySelectorAll(\"[data-reactive-tags-list]\")].find(owns)\n if (!list) return\n\n const template = [...c.root.querySelectorAll(\"[data-reactive-tags-template]\")].find(owns)\n const chipProto = template?.content?.firstElementChild\n if (!chipProto) {\n if (!c.tagsWarnedTemplate) {\n console.warn(\n \"[phlex-reactive] reactive_tags: no chip <template data-reactive-tags-template> found in this root — \" +\n \"chips will not render (the hidden field still updates). Add a template with a \" +\n \"[data-reactive-tag-text] node and a reactive_tags_remove button.\"\n )\n c.tagsWarnedTemplate = true\n }\n return\n }\n\n while (list.firstChild) list.removeChild(list.firstChild)\n for (const tag of tags) {\n const chip = chipProto.cloneNode(true)\n chip.setAttribute?.(\"data-reactive-tag\", tag)\n const sink = chip.matches?.(\"[data-reactive-tag-text]\")\n ? chip\n : (chip.querySelectorAll?.(\"[data-reactive-tag-text]\") ?? [])[0]\n if (sink) sink.textContent = tag\n const removers = [...(chip.querySelectorAll?.('[data-action*=\"reactive#tagsRemove\"]') ?? [])]\n if (chip.matches?.('[data-action*=\"reactive#tagsRemove\"]')) removers.push(chip)\n for (const remover of removers) remover.setAttribute?.(\"data-reactive-tag-param\", tag)\n list.appendChild(chip)\n }\n}\n\n// Hide + mark every owned option whose DECLARED tag is already selected\n// (data-reactive-tags-selected — #syncFilter keeps it hidden through\n// re-filters), and resurface an option WE hid when its tag is removed. Only\n// marker-carrying options are un-hidden — an option hidden by the filter or\n// the server stays as-is. With a filter bound, one final #syncFilter re-folds\n// groups/empty against the new selected set.\nfunction tagsMarkOptions(c, tags, owns) {\n const selected = new Set(tags.map((tag) => tag.toLowerCase()))\n for (const el of c.root.querySelectorAll(\"[role=option]\")) {\n if (!owns(el)) continue\n const tag = el.getAttribute?.(\"data-reactive-tag-param\")\n if (!tag) continue\n if (selected.has(tag.toLowerCase())) {\n el.setAttribute(\"data-reactive-tags-selected\", \"true\")\n el.hidden = true\n el.removeAttribute?.(\"data-reactive-highlighted\")\n } else if (el.hasAttribute?.(\"data-reactive-tags-selected\")) {\n el.removeAttribute(\"data-reactive-tags-selected\")\n if (!filterEnabled(c)) el.hidden = false\n }\n }\n if (filterEnabled(c)) syncFilter(c)\n}\n\n// A fresh index per nested-row add (issue #208) — strictly monotonic and\n// clock-seeded, so it can never collide with server-rendered integer indexes\n// (0..n) NOR with a rapid same-millisecond double add.\nfunction nextNestedIndex(c) {\n c.nestedIndex = Math.max(c.nestedIndex + 1, Date.now())\n return c.nestedIndex\n}\n\n// Swap every NEW_ROW in the clone's name/id/for for the fresh index, so the\n// row posts as its own `…_attributes[<index>][field]` group and labels keep\n// pointing at their (renumbered) inputs.\nfunction renumberNestedRow(row, index) {\n const nodes = [row, ...(row.querySelectorAll?.(\"*\") ?? [])]\n for (const el of nodes) {\n for (const attr of [\"name\", \"id\", \"for\"]) {\n const value = el.getAttribute?.(attr)\n if (value && value.includes(\"NEW_ROW\")) el.setAttribute?.(attr, value.replaceAll(\"NEW_ROW\", String(index)))\n }\n }\n}\n\n// A half-built nested-rows binding should be loud, but never per-click.\nfunction warnNestedOnce(c, assoc) {\n if (c.nestedWarned) return\n console.warn(\n `[phlex-reactive] nested rows: no owned [data-reactive-nested-list=\"${assoc}\"] container + ` +\n `<template data-reactive-nested-template=\"${assoc}\"> pair found in this root — the add ` +\n \"trigger did nothing. Render both inside the same reactive root (reactive_nested_list / \" +\n \"reactive_nested_template).\"\n )\n c.nestedWarned = true\n}\n\n// Fill-then-add (issue #208): copy each source control's value into the\n// matching cloned-row field, keyed by the trailing bracket segment of the\n// field's name (#nestedJsonKey — the SAME inference JSON mode uses, so the\n// two features can't drift). Sources resolve root-scoped and owned (#15); an\n// unresolved source or an unmatched key is silently skipped (the row still\n// adds — a half-mapped binding must never throw on click). Returns the FIRST\n// source control read (for focus), or null. With `clear`, resets every source\n// it read via the set-value + dispatch contract (#183) so dirty/show/compute\n// observe the reset.\nfunction seedNestedRow(c, row, fromJson, clear) {\n if (!fromJson) return null\n let map\n try {\n map = JSON.parse(fromJson)\n } catch {\n return null\n }\n if (!map || typeof map !== \"object\") return null\n\n const owns = c.core.ownership()\n const rowFields = [...(row.querySelectorAll?.(\"input, select, textarea\") ?? [])]\n const sources = []\n for (const [key, selector] of Object.entries(map)) {\n const source = [...(c.root.querySelectorAll?.(selector) ?? [])].find(owns)\n if (!source) continue\n const target = rowFields.find((field) => nestedJsonKey(field.getAttribute?.(\"name\")) === key)\n if (!target) continue\n seedNestedField(target, source)\n sources.push(source)\n }\n if (clear) for (const source of sources) clearNestedSource(source)\n return sources[0] ?? null\n}\n\n// Copy a source control's value into a cloned-row field, then dispatch a\n// bubbling `input` (the set-value + dispatch contract, #183). Checkbox ↔\n// checkbox copies the checked state; every other target takes the source's\n// submit-shaped value (#nestedFieldValue), so a checkbox source feeding a\n// text field lands \"on\"/\"\" exactly as a submit would.\nfunction seedNestedField(target, source) {\n if (target.type === \"checkbox\") {\n target.checked = source.type === \"checkbox\" ? !!source.checked : nestedFieldValue(source) !== \"\"\n } else {\n target.value = nestedFieldValue(source)\n }\n fireInput(target)\n}\n\n// Reset a source control after a fill-then-add (issue #208), dispatching a\n// bubbling `input` so dirty tracking / reactive_show / compute see the reset.\nfunction clearNestedSource(source) {\n if (source.type === \"checkbox\") source.checked = false\n else source.value = \"\"\n fireInput(source)\n}\n\n// Resolve %{field} placeholders in a confirm message from a row's field map\n// (issue #222). Ruby-style %{name} tokens; an unresolved key is left verbatim\n// (a visible, debuggable placeholder — never an empty hole or a throw). A\n// message with no placeholders returns unchanged, so this is inert for every\n// server-rendered (already-interpolated) confirm string.\nfunction interpolateConfirm(message, fields) {\n if (!message.includes(\"%{\")) return message\n return message.replace(/%\\{(\\w+)\\}/g, (whole, key) =>\n Object.prototype.hasOwnProperty.call(fields, key) ? fields[key] : whole,\n )\n}\n\n// The remove itself, shared by the confirmed and no-confirm paths. Draft rows\n// leave the DOM; a persisted row (a hidden [_destroy] input present) is marked\n// \"1\" + hidden instead (set-value + dispatch contract, #183), so Rails destroys\n// it on save. Then re-sync every owned JSON-mode list (#208) — an absent row\n// IS the removal; a form without a JSON list iterates an empty set and exits.\nfunction removeNestedRow(c, row) {\n const destroy = [...(row.querySelectorAll?.('input[name$=\"[_destroy]\"]') ?? [])][0]\n if (destroy) {\n destroy.value = \"1\"\n fireInput(destroy)\n row.hidden = true\n } else {\n row.parentNode?.removeChild?.(row)\n }\n\n syncAllNestedJson(c)\n}\n\n// Serialize every owned JSON-mode list into its hidden field. The connect\n// seed and the input/remove re-syncs funnel through here so one place owns\n// the \"DOM rows → JSON field\" projection.\nfunction syncAllNestedJson(c) {\n if (typeof c.root?.querySelectorAll !== \"function\") return\n const owns = c.core.ownership()\n for (const list of [...c.root.querySelectorAll(\"[data-reactive-nested-json]\")].filter(owns)) {\n syncNestedJsonList(c, list.getAttribute(\"data-reactive-nested-json\"))\n }\n}\n\n// The hidden field a JSON-mode list mirrors into — resolved fresh (a morph\n// replaces nodes, never cache it) and OWNED by this root (#15). null when\n// the selector resolves nothing, so the caller no-ops (a half-built binding\n// must never break the page).\nfunction nestedJsonField(c, list) {\n const selector = list.getAttribute?.(\"data-reactive-nested-json-field\")\n if (!selector) return null\n const owns = c.core.ownership()\n return [...c.root.querySelectorAll(selector)].find(owns) ?? null\n}\n\n// One row → { key: value } over its named form controls. The JSON key is the\n// trailing bracket segment of each control's name (order[todos_attributes]\n// [3][title] → \"title\"; a bare `title` → \"title\"), the \"infer from input\n// names\" contract. The [_destroy] control is dropped (JSON has no destroy\n// marker). Later inputs with the same key win (last-wins, the DOM order).\n// Like a real submit (#299): an unchecked radio, a disabled control (its own\n// flag or a <fieldset disabled> ancestor: :disabled) and a nested reactive\n// root's control are skipped.\nfunction nestedRowObject(c, row) {\n const obj = {}\n const owns = c.core.ownership()\n for (const el of [...(row.querySelectorAll?.(\"input, select, textarea\") ?? [])]) {\n if (el.matches(\":disabled\") || (el.type === \"radio\" && !el.checked) || !owns(el)) continue\n const key = nestedJsonKey(el.getAttribute?.(\"name\"))\n if (key === null || key === \"_destroy\") continue\n obj[key] = nestedFieldValue(el)\n }\n return obj\n}\n\n// The trailing bracket segment of a field name (the inferred JSON key), or\n// the bare name when it carries no brackets. null for a nameless control\n// (a bare button, an unnamed helper input) — skipped by the caller.\nfunction nestedJsonKey(name) {\n if (!name) return null\n const match = name.match(/\\[([^\\][]+)\\]$/)\n return match ? match[1] : name\n}\n\n// A form control's submitted value: an unchecked checkbox contributes \"\" (it\n// wouldn't post at all), a checked one its value (default \"on\"); everything\n// else its .value. Keeps the JSON shape close to what a real form submit\n// would carry for the same control.\nfunction nestedFieldValue(el) {\n if (el.type === \"checkbox\") return el.checked ? (el.value || \"on\") : \"\"\n return el.value ?? \"\"\n}\n\n// Project ONE JSON-mode list's surviving rows into its hidden field. Each\n// row becomes an object keyed by the trailing bracket segment of its inputs'\n// names (…[title] → \"title\"); a hidden/_destroy-marked row is skipped (an\n// absent row IS the removal — JSON carries no destroy marker). The write\n// uses the set-value + dispatch contract (issue #183) so dirty tracking,\n// reactive_show, and compute see the change — but only when the value\n// actually changed, so a connect seed on an already-correct field is silent.\nfunction syncNestedJsonList(c, assoc) {\n const owns = c.core.ownership()\n const list = [...c.root.querySelectorAll(`[data-reactive-nested-list=\"${assoc}\"]`)].find(owns)\n if (!list) return\n const field = nestedJsonField(c, list)\n if (!field) return\n\n const rows = []\n for (const row of [...(list.querySelectorAll?.(\"[data-reactive-nested-row]\") ?? [])]) {\n if (!owns(row) || row.hidden) continue\n rows.push(nestedRowObject(c, row))\n }\n\n const next = JSON.stringify(rows)\n if (field.value === next) return\n field.value = next\n fireInput(field)\n}\n\n// Tag-chip input (issue #203) — the composed combobox/tags primitive. The\n// root's data-reactive-tags-field names the hidden input that stores the\n// COMMA-JOINED value; these three actions are its only writers. All of it is\n// FORM state (like text in an input) — no token, no POST: the surrounding\n// form submit carries the joined value. The chip list is re-projected from\n// the field on every write (#syncTags), so the field stays the single source\n// of truth.\n//\n// Enter on the query input: add the TYPED text — unless this Enter belongs\n// to listnav (reactive_tags_add composes after reactive_listnav on the same\n// keydown.enter). Two guards make the composition order-independent:\n// defaultPrevented means listnavPick ALREADY picked the highlighted option\n// (adding the typed text too would double-add); a still-visible highlighted\n// option means listnavPick is ABOUT to pick it (when tagsAdd is bound\n// first). Past the guards, Enter is OURS — preventDefault unconditionally so\n// it can never submit the enclosing form (blank input included). A\n// comma-separated paste splits into individual tags (the value is\n// comma-joined, so a comma can never be part of one tag). The input clears\n// only when something was actually added — a duplicate keeps the typed text\n// for correction.\nexport function tagsAdd(controller, core, event) {\n const c = ctx(controller, core)\n if (!tagsEnabled(c)) return\n if (event?.defaultPrevented) return\n if (c.core.listnavOptions(event).some((el) => el.hasAttribute?.(\"data-reactive-highlighted\"))) return\n event?.preventDefault?.()\n\n const input = event?.currentTarget ?? event?.target\n if (!input) return\n const added = tagsAddValues(c, String(input.value ?? \"\").split(\",\"))\n if (!added) return\n input.value = \"\"\n if (filterEnabled(c)) syncFilter(c)\n}\n\n// Click (or listnav Enter, which CLICKS the highlighted option) on a\n// preloaded option: add its DECLARED tag (data-reactive-tag-param — set by\n// reactive_tags_option, never free text). After a successful add, reset the\n// query so the next tag starts from the full list: clear the filter input,\n// re-narrow, and hand focus back for continued typing.\nexport function tagsPick(controller, core, event) {\n const c = ctx(controller, core)\n if (!tagsEnabled(c)) return\n event?.preventDefault?.()\n\n const trigger = event?.currentTarget ?? event?.target\n const tag = trigger?.getAttribute?.(\"data-reactive-tag-param\")\n if (!tag) return\n if (!tagsAddValues(c, [tag])) return\n\n const input = tagsQueryInput(c)\n if (!input) return\n input.value = \"\"\n syncFilter(c)\n input.focus?.()\n}\n\n// Click on a chip's remove button: drop its tag (case-insensitive match, the\n// dedupe convention) from the hidden value. The re-projection removes the\n// chip and resurfaces the option. Removing an absent tag is a no-op.\nexport function tagsRemove(controller, core, event) {\n const c = ctx(controller, core)\n if (!tagsEnabled(c)) return\n event?.preventDefault?.()\n\n const trigger = event?.currentTarget ?? event?.target\n const tag = trigger?.getAttribute?.(\"data-reactive-tag-param\")\n if (!tag) return\n const field = tagsField(c)\n if (!field) return\n\n const tags = tagsRead(field)\n const next = tags.filter((t) => t.toLowerCase() !== tag.toLowerCase())\n if (next.length === tags.length) return\n tagsWrite(c, field, next)\n}\n\n// Draft nested-attribute rows (issue #208) — the \"new parent + child rows\"\n// window. The rows are FORM state (the reactive_tags posture): no token, no\n// POST, ever — the surrounding REAL form submit carries Rails'\n// accepts_nested_attributes_for names and the server reconciles parent +\n// rows in ONE create. Add clones the association's server-owned\n// <template data-reactive-nested-template=\"assoc\"> row, swaps every NEW_ROW\n// in the clone's name/id/for for a fresh unique index (each row posts as its\n// own `…_attributes[<index>][field]` group), appends it to the owned\n// [data-reactive-nested-list=\"assoc\"] container, and focuses the new row's\n// first field. Several collections can share one root — everything is keyed\n// by the association name the trigger carries.\nexport function nestedAdd(controller, core, event) {\n const c = ctx(controller, core)\n event?.preventDefault?.()\n const trigger = event?.currentTarget ?? event?.target\n const assoc = trigger?.getAttribute?.(\"data-reactive-association-param\")\n if (!assoc) return\n if (typeof c.root?.querySelectorAll !== \"function\") return\n\n const owns = c.core.ownership()\n const list = [...c.root.querySelectorAll(`[data-reactive-nested-list=\"${assoc}\"]`)].find(owns)\n const template = [...c.root.querySelectorAll(`[data-reactive-nested-template=\"${assoc}\"]`)].find(owns)\n const proto = template?.content?.firstElementChild\n if (!list || !proto) {\n warnNestedOnce(c, assoc)\n return\n }\n\n const row = proto.cloneNode(true)\n renumberNestedRow(row, nextNestedIndex(c))\n list.appendChild(row)\n\n // Fill-then-add (issue #208 Scenario A): seed the cloned row from named\n // source controls OUTSIDE the row, then (optionally) clear the sources.\n // Runs AFTER renumber+append so a seeded field's name already carries its\n // final `[<index>][field]` form — the key match agrees with what JSON mode\n // reads. `seeded` is the FIRST source we cleared/read, so fill-then-add can\n // return focus to the sources instead of stealing it into the new row.\n const fromJson = trigger?.getAttribute?.(\"data-reactive-nested-from-param\")\n const clear = trigger?.getAttribute?.(\"data-reactive-nested-clear-param\") === \"true\"\n const firstSource = seedNestedRow(c, row, fromJson, clear)\n\n // Focus: inline-edit (no from:) focuses the new row's first field so you\n // type INTO it; fill-then-add keeps focus on the sources (the first one) so\n // you keep entering the next item — stealing focus would break that loop.\n if (fromJson) firstSource?.focus?.()\n else [...(row.querySelectorAll?.(\"input, select, textarea\") ?? [])][0]?.focus?.()\n\n // JSON mode (issue #208): a freshly-added row must land in the hidden field\n // immediately (seeded values included), so the serialized array reflects the\n // DOM even before the first keystroke. A no-op when not `as: :json`.\n if (list.getAttribute?.(\"data-reactive-nested-json\") === assoc) syncNestedJsonList(c, assoc)\n}\n\n// Remove the trigger's closest row wrapper. A DRAFT row (no [_destroy]\n// input) leaves the DOM — it was never persisted, so removing its fields IS\n// the removal. A PERSISTED row (an edit form rendered a hidden [_destroy]\n// input via nested_field_name) is marked \"1\" and hidden instead — Rails\n// destroys it on save. The mark dispatches a real bubbling `input` (the\n// set-value + dispatch contract, issue #183) so dirty tracking/compute see it.\nexport function nestedRemove(controller, core, event) {\n const c = ctx(controller, core)\n event?.preventDefault?.()\n const trigger = event?.currentTarget ?? event?.target\n const row = trigger?.closest?.(\"[data-reactive-nested-row]\")\n if (!row) return\n // The closest() walk must not escape this root — a root can itself sit\n // inside ANOTHER collection's row (the issue #15 closest-form posture).\n if (row.closest?.('[data-controller~=\"reactive\"]') !== c.root) return\n\n // Confirm gate (issue #218): reactive_nested_remove(confirm:) emits the SAME\n // data-reactive-confirm[-when]-param the other triggers do (nestedRemove reads\n // params via getAttribute, not event.params, so pull them off the trigger),\n // routed through the SAME #effectiveConfirmMessage + confirmResolver seam. A\n // static string always shows; a conditional Hash fires only when it matches,\n // else null. No confirm attr → null → the immediate-remove fast path.\n const confirm = trigger?.getAttribute?.(\"data-reactive-confirm-param\")\n const confirmWhen = trigger?.getAttribute?.(\"data-reactive-confirm-when-param\")\n const rawMessage = confirm || (confirmWhen ? conditionalConfirmMessage(c, confirmWhen) : null)\n if (!rawMessage) return removeNestedRow(c, row)\n\n // Per-row confirm interpolation (issue #222). A row added client-side is a\n // cloneNode of the <template>, and the clone carries the TEMPLATE's confirm\n // string verbatim — the renumber/seed steps never rewrite the confirm attr.\n // So resolve %{field} placeholders here, from THIS row's live field values\n // (read now, not at clone time, so a later edit is reflected). An unresolved\n // key is left as its literal %{key} (debuggable, never throws). Server-\n // rendered rows already interpolate server-side, so their finished strings\n // carry no %{}; this is a no-op for them.\n const fields = nestedRowObject(c, row)\n const message = interpolateConfirm(rawMessage, fields)\n\n // Gate through the overridable confirmResolver (issues #52/#55/#178) — a\n // themed dialog set with setConfirmResolver covers this trigger too. Pass the\n // row context (issue #222, superset of proposal 3) as an optional 2nd arg so\n // a power-user override can build the string itself; the message is already\n // interpolated for the default window.confirm path. Call the resolver INSIDE\n // the chain so even a SYNCHRONOUS override throw is a cancel (like a dismissed\n // dialog), and remove ONLY on a truthy resolution.\n return c.core.confirm(message, { el: trigger, row, fields }).then((ok) => {\n if (ok) removeNestedRow(c, row)\n })\n}\n\n// JSON-mode nested rows (issue #208) — the delegated input/change handler.\n// An app whose controller parses a serialized JSON param instead of Rails'\n// accepts_nested_attributes_for opts a list into `as: :json`; the client\n// then mirrors that list's rows into ONE hidden field as a JSON array on\n// every owned edit. Public so Stimulus can bind it; a no-op unless the\n// edited field belongs to a JSON-mode list this root owns.\nfunction syncNestedJson(c, event) {\n const target = event?.target\n if (!target || !c.core.owns(target)) return\n // Re-serialize every JSON-mode list (an edit could touch any of them; the\n // per-list owned-row scan is cheap and keeps this handler association-free).\n syncAllNestedJson(c)\n}\n\n// The numeric threshold keys (issue #176 part B) — the client half of the Ruby\n// SHOW_NUMERIC_KEYS. Order-independent; the evaluator reads the one that's\n// present. Each coerces BOTH sides to Number and compares.\nconst SHOW_NUMERIC_KEYS = [\"gte\", \"gt\", \"lte\", \"lt\"]\n\n// The length predicate keys (issue #226) — the client half of Ruby's\n// ShowConditions::LENGTH_KEYS. Length is counted in CODEPOINTS\n// ([...str].length), NOT UTF-16 code units (str.length), so Ruby's\n// String#length and this evaluator agree on multibyte values — the shared\n// fixture's emoji vector proves it.\nconst SHOW_LENGTH_KEYS = [\"len_eq\", \"len_gte\", \"len_gt\", \"len_lte\", \"len_lt\"]\n\n// The checked-count keys (issue #319) — the client half of Ruby's\n// ShowConditions::CHECKED_KEYS: how many owned boxes of a group are ticked.\nconst SHOW_CHECKED_KEYS = [\"checked_eq\", \"checked_gte\", \"checked_gt\", \"checked_lte\", \"checked_lt\"]\n\n// Evaluate one count predicate (len_* or checked_*). A count is a TOTAL\n// function (blank/absent → 0), so every field value is decidable — no\n// fail-closed special case like the numeric thresholds ({ length: 0 }\n// legitimately matches a blank field). A non-Integer LITERAL is a malformed\n// binding — warn-skip (null), default-deny.\nfunction countPredicateMatches(key, literal, count) {\n if (!Number.isInteger(literal)) {\n console.warn(`[phlex-reactive] reactive_show ${key}: needs an integer literal, got ${JSON.stringify(literal)} — skipped`)\n return null\n }\n switch (key.slice(key.indexOf(\"_\") + 1)) {\n case \"eq\":\n return count === literal\n case \"gte\":\n return count >= literal\n case \"gt\":\n return count > literal\n case \"lte\":\n return count <= literal\n case \"lt\":\n return count < literal\n default:\n return null\n }\n}\n\n// Evaluate one numeric threshold predicate against a field value. Returns\n// true/false for a decidable comparison, or null when the LITERAL itself is\n// non-numeric (a malformed binding — warn-skip, default-deny). A non-numeric\n// FIELD value (empty/blank/garbage) is treated as NaN → false: the\n// reveal-on-threshold notice stays hidden, the safe default. Shared by the\n// owned-binding evaluator (raw string literal off an attr) and the\n// cross-root/compound evaluator (a literal that arrived as a JSON number or\n// string).\nfunction numericPredicateMatches(key, literal, value) {\n const rhs = Number(literal)\n if (Number.isNaN(rhs)) {\n console.warn(`[phlex-reactive] reactive_show ${key}: needs a numeric literal, got ${JSON.stringify(literal)} — skipped`)\n return null\n }\n // A blank/whitespace field value must fail closed. Number(\"\") and\n // Number(\" \") are 0 (NOT NaN), so a bare Number()+isNaN check would wrongly\n // reveal a `lte:`/`lt:`/`gte: 0` binding on an EMPTY field. Force the\n // empty/blank case to NaN so the \"blank → hidden\" contract holds for every\n // operator, not just the ones where 0 happens to fail the comparison.\n const trimmed = value == null ? \"\" : String(value).trim()\n const n = trimmed === \"\" ? NaN : Number(trimmed)\n if (Number.isNaN(n)) return false\n switch (key) {\n case \"gte\":\n return n >= rhs\n case \"gt\":\n return n > rhs\n case \"lte\":\n return n <= rhs\n case \"lt\":\n return n < rhs\n default:\n return null\n }\n}\n\n// Evaluate an ALREADY-PARSED show predicate object (issue #164) — the\n// reactive_show_targets map embeds { equals/not/in } directly in its JSON, so\n// unlike showBindingMatches there are no attrs to read or re-parse. The same\n// literal-only vocabulary; anything else (empty, unknown keys, a non-array\n// in:) returns null and the caller warn-skips that target (default-deny — a\n// hand-built map entry must never flip visibility it doesn't declare).\nfunction showPredicateMatches(pred, value) {\n if (!pred || typeof pred !== \"object\") return null\n if (typeof pred.equals === \"string\") return value === pred.equals\n if (typeof pred.not === \"string\") return value !== pred.not\n if (Array.isArray(pred.in)) return pred.in.includes(value)\n // Numeric threshold predicates (issue #176 part B): the literal arrives as a\n // JSON number (or a numeric string) embedded in the predicate object — one\n // shared numericPredicateMatches with the owned-binding evaluator.\n for (const key of SHOW_NUMERIC_KEYS) {\n if (key in pred) return numericPredicateMatches(key, pred[key], value)\n }\n // Length predicates (issue #226): codepoint count vs an Integer literal.\n for (const key of SHOW_LENGTH_KEYS) {\n if (key in pred) return countPredicateMatches(key, pred[key], [...String(value ?? \"\")].length)\n }\n return null\n}\n\n// Evaluate one DNF TERM against a resolved field value (issue #180). A missing\n// field (null) or a malformed/unknown predicate folds to FALSE — fail-closed\n// (default-deny): a broken AND term can't pass, a broken OR term can't reveal.\nfunction dnfTermMatches(term, fieldValue) {\n if (!term || typeof term !== \"object\" || typeof term.field !== \"string\") return false\n // A checked_* term (issue #319) counts the group's ticked boxes: the\n // resolver's count form, or — from collected fields (a conditional confirm)\n // — the group's array of checked values (a lone box's true/false is 1/0).\n const checked = SHOW_CHECKED_KEYS.find((key) => key in term)\n if (checked) {\n const n = fieldValue(term.field, true)\n return countPredicateMatches(checked, term[checked], Array.isArray(n) ? n.length : Number(n) || 0) === true\n }\n // An absent owned field reads as \"\" — identical to the server evaluator\n // (ShowConditions.match? treats a missing field as blank). This keeps the\n // Ruby first-paint and the client live-toggle in exact agreement (the shared\n // fixture proves it). A malformed predicate still folds to false.\n const value = fieldValue(term.field) ?? \"\"\n return showPredicateMatches(term, value) === true\n}\n\n// Evaluate a DNF show payload (issue #180): { any: [group, …] } where each\n// GROUP is an array of terms (terms AND within a group, groups OR). Returns\n// true/false for a decidable payload, or null for a malformed one (no groups)\n// so the caller warn-skips and leaves visibility alone. This is the ONE shape\n// the 0.10 wire emits; showPayloadMatches routes the legacy shapes here or to\n// the compatibility arm below.\nfunction anyOfAllsMatches(groups, fieldValue) {\n if (!Array.isArray(groups) || groups.length === 0) return null\n // groups OR; within a group, terms AND (an empty group can't decide → false).\n return groups.some((group) => Array.isArray(group) && group.length > 0 &&\n group.every((term) => dnfTermMatches(term, fieldValue)))\n}\n\n// Every field a DNF payload's groups reference (issue #209) — drives the\n// \"leave the target alone when NO referenced field is owned\" skip, the\n// single-field-target skip generalized. Returns null for a malformed payload\n// (no groups, or no term names a field) so the caller warn-skips instead of\n// toggling on garbage (default-deny, like every other malformed-wire arm).\nfunction dnfGroupFields(groups) {\n if (!Array.isArray(groups) || groups.length === 0) return null\n const fields = new Set()\n for (const group of groups) {\n if (!Array.isArray(group)) continue\n for (const term of group) {\n if (term && typeof term === \"object\" && typeof term.field === \"string\") fields.add(term.field)\n }\n }\n return fields.size > 0 ? [...fields] : null\n}\n\n// Whether every term of a DNF payload is a checked_* count (issue #348) —\n// the payload a group with zero owned boxes still decides (count 0).\nfunction countsOnly(groups) {\n return groups.flat().every((term) => SHOW_CHECKED_KEYS.some((key) => key in Object(term)))\n}\n\n// Resolve the effective confirm message (issue #179). A plain string is the\n// static #52 form (always shown). A confirmWhen JSON payload is the CONDITIONAL\n// form — evaluated over the SAME collected fields reactive_compute reads — and\n// returns the message ONLY when it fires, else null (proceed, no dialog):\n// { groups, message } — the reactive_show conditions fold (anyOfAllsMatches)\n// { predicate, message } — a registered fn (setConfirmPredicate) over the fields\n// A missing predicate warns and returns null (PROCEED without a dialog) — the\n// compute unknown-reducer posture. This is soft-validation UX; the endpoint's\n// authorize/default-deny is the real gate, so failing OPEN here never grants\n// anything the server wouldn't already allow.\nfunction conditionalConfirmMessage(c, confirmWhen) {\n\n // Stimulus auto-parses a JSON-object -param value, so confirmWhen usually\n // arrives ALREADY parsed. Accept an object as-is; parse a string defensively\n // (a hand-built attr, or a non-Stimulus caller). A malformed string warns and\n // proceeds without a dialog (default-deny UX — the server is the real gate).\n let payload = confirmWhen\n if (typeof confirmWhen === \"string\") {\n try {\n payload = JSON.parse(confirmWhen)\n } catch {\n console.warn(`[phlex-reactive] malformed conditional confirm payload ${JSON.stringify(confirmWhen)} — skipped`)\n return null\n }\n }\n if (!payload || typeof payload !== \"object\") return null\n\n const { fields } = c.core.collectFields()\n const fieldValue = (name) => fields[name]\n\n let fires\n if (typeof payload.predicate === \"string\") {\n const fn = confirmPredicate(payload.predicate)\n if (!fn) {\n console.warn(`[phlex-reactive] confirm predicate \"${payload.predicate}\" is not registered — proceeding without a dialog (register it with setConfirmPredicate)`)\n return null\n }\n fires = !!fn(fields)\n } else {\n // Declarative: the DNF groups fold, identical to reactive_show — matches → fire.\n fires = anyOfAllsMatches(payload.groups?.any, fieldValue) === true\n }\n\n return fires ? payload.message : null\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "5A6A0FBFE381B39A64756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "D5F37BFC1A84F81D64756E2164756E21",
9
9
  "names": []
10
10
  }