stimeo-ui 0.15.0 → 0.16.0
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.
- package/CHANGELOG.md +178 -0
- package/README.md +120 -0
- package/dist/cable/index.js +63 -15
- package/dist/cable/index.js.map +1 -1
- package/dist/controllers/accordion_controller.d.ts +30 -3
- package/dist/controllers/accordion_controller.js +98 -9
- package/dist/controllers/accordion_controller.js.map +1 -1
- package/dist/controllers/alert_dialog_controller.d.ts +8 -7
- package/dist/controllers/alert_dialog_controller.js.map +1 -1
- package/dist/controllers/announcer_controller.d.ts +3 -2
- package/dist/controllers/announcer_controller.js +8 -1
- package/dist/controllers/announcer_controller.js.map +1 -1
- package/dist/controllers/auto_submit_controller.js +43 -2
- package/dist/controllers/auto_submit_controller.js.map +1 -1
- package/dist/controllers/avatar_controller.js +1 -1
- package/dist/controllers/avatar_controller.js.map +1 -1
- package/dist/controllers/breadcrumb_controller.d.ts +10 -7
- package/dist/controllers/breadcrumb_controller.js +9 -8
- package/dist/controllers/breadcrumb_controller.js.map +1 -1
- package/dist/controllers/bulk_select_controller.d.ts +4 -4
- package/dist/controllers/bulk_select_controller.js +7 -6
- package/dist/controllers/bulk_select_controller.js.map +1 -1
- package/dist/controllers/calendar_controller.d.ts +85 -23
- package/dist/controllers/calendar_controller.js +323 -123
- package/dist/controllers/calendar_controller.js.map +1 -1
- package/dist/controllers/carousel_controller.d.ts +67 -22
- package/dist/controllers/carousel_controller.js +263 -38
- package/dist/controllers/carousel_controller.js.map +1 -1
- package/dist/controllers/character_counter_controller.d.ts +1 -1
- package/dist/controllers/character_counter_controller.js +40 -2
- package/dist/controllers/character_counter_controller.js.map +1 -1
- package/dist/controllers/checkbox_controller.js +54 -10
- package/dist/controllers/checkbox_controller.js.map +1 -1
- package/dist/controllers/clipboard_controller.d.ts +28 -2
- package/dist/controllers/clipboard_controller.js +63 -5
- package/dist/controllers/clipboard_controller.js.map +1 -1
- package/dist/controllers/collapsible_controller.d.ts +25 -1
- package/dist/controllers/collapsible_controller.js +99 -14
- package/dist/controllers/collapsible_controller.js.map +1 -1
- package/dist/controllers/color_picker_controller.d.ts +29 -8
- package/dist/controllers/color_picker_controller.js +80 -34
- package/dist/controllers/color_picker_controller.js.map +1 -1
- package/dist/controllers/combobox_controller.d.ts +10 -1
- package/dist/controllers/combobox_controller.js +106 -16
- package/dist/controllers/combobox_controller.js.map +1 -1
- package/dist/controllers/command_palette_controller.d.ts +3 -3
- package/dist/controllers/command_palette_controller.js +35 -3
- package/dist/controllers/command_palette_controller.js.map +1 -1
- package/dist/controllers/conditional_fields_controller.js +56 -11
- package/dist/controllers/conditional_fields_controller.js.map +1 -1
- package/dist/controllers/confirm_controller.js +3 -0
- package/dist/controllers/confirm_controller.js.map +1 -1
- package/dist/controllers/context_menu_controller.d.ts +6 -0
- package/dist/controllers/context_menu_controller.js +32 -12
- package/dist/controllers/context_menu_controller.js.map +1 -1
- package/dist/controllers/countdown_controller.d.ts +30 -1
- package/dist/controllers/countdown_controller.js +129 -26
- package/dist/controllers/countdown_controller.js.map +1 -1
- package/dist/controllers/currency_input_controller.d.ts +74 -15
- package/dist/controllers/currency_input_controller.js +205 -58
- package/dist/controllers/currency_input_controller.js.map +1 -1
- package/dist/controllers/data_grid_controller.d.ts +63 -18
- package/dist/controllers/data_grid_controller.js +195 -29
- package/dist/controllers/data_grid_controller.js.map +1 -1
- package/dist/controllers/date_range_picker_controller.d.ts +34 -6
- package/dist/controllers/date_range_picker_controller.js +139 -27
- package/dist/controllers/date_range_picker_controller.js.map +1 -1
- package/dist/controllers/dialog_controller.d.ts +10 -3
- package/dist/controllers/dialog_controller.js +35 -8
- package/dist/controllers/dialog_controller.js.map +1 -1
- package/dist/controllers/direct_upload_controller.js +22 -4
- package/dist/controllers/direct_upload_controller.js.map +1 -1
- package/dist/controllers/dirty_form_controller.d.ts +2 -2
- package/dist/controllers/dirty_form_controller.js +14 -1
- package/dist/controllers/dirty_form_controller.js.map +1 -1
- package/dist/controllers/dismissible_controller.js +1 -0
- package/dist/controllers/dismissible_controller.js.map +1 -1
- package/dist/controllers/drawer_controller.d.ts +18 -10
- package/dist/controllers/drawer_controller.js +54 -19
- package/dist/controllers/drawer_controller.js.map +1 -1
- package/dist/controllers/dropdown_controller.d.ts +9 -3
- package/dist/controllers/dropdown_controller.js +36 -9
- package/dist/controllers/dropdown_controller.js.map +1 -1
- package/dist/controllers/editable_controller.js +34 -0
- package/dist/controllers/editable_controller.js.map +1 -1
- package/dist/controllers/file_dropzone_controller.js +144 -51
- package/dist/controllers/file_dropzone_controller.js.map +1 -1
- package/dist/controllers/filter_controller.d.ts +10 -4
- package/dist/controllers/filter_controller.js +20 -6
- package/dist/controllers/filter_controller.js.map +1 -1
- package/dist/controllers/flash_controller.d.ts +26 -6
- package/dist/controllers/flash_controller.js +305 -46
- package/dist/controllers/flash_controller.js.map +1 -1
- package/dist/controllers/focus_controller.js +1 -0
- package/dist/controllers/focus_controller.js.map +1 -1
- package/dist/controllers/form_field_controller.js +7 -5
- package/dist/controllers/form_field_controller.js.map +1 -1
- package/dist/controllers/form_validation_controller.js +19 -13
- package/dist/controllers/form_validation_controller.js.map +1 -1
- package/dist/controllers/frame_loading_controller.js +16 -2
- package/dist/controllers/frame_loading_controller.js.map +1 -1
- package/dist/controllers/highlight_controller.js +19 -2
- package/dist/controllers/highlight_controller.js.map +1 -1
- package/dist/controllers/hover_card_controller.d.ts +10 -2
- package/dist/controllers/hover_card_controller.js +40 -14
- package/dist/controllers/hover_card_controller.js.map +1 -1
- package/dist/controllers/idle_controller.d.ts +16 -3
- package/dist/controllers/idle_controller.js +90 -5
- package/dist/controllers/idle_controller.js.map +1 -1
- package/dist/controllers/input_mask_controller.d.ts +5 -2
- package/dist/controllers/input_mask_controller.js +65 -9
- package/dist/controllers/input_mask_controller.js.map +1 -1
- package/dist/controllers/intersection_controller.js +3 -0
- package/dist/controllers/intersection_controller.js.map +1 -1
- package/dist/controllers/lazy_frame_controller.js +11 -2
- package/dist/controllers/lazy_frame_controller.js.map +1 -1
- package/dist/controllers/listbox_controller.d.ts +50 -8
- package/dist/controllers/listbox_controller.js +203 -45
- package/dist/controllers/listbox_controller.js.map +1 -1
- package/dist/controllers/masonry_controller.js +0 -2
- package/dist/controllers/masonry_controller.js.map +1 -1
- package/dist/controllers/menu_controller.d.ts +9 -3
- package/dist/controllers/menu_controller.js +45 -16
- package/dist/controllers/menu_controller.js.map +1 -1
- package/dist/controllers/menubar_controller.d.ts +11 -0
- package/dist/controllers/menubar_controller.js +58 -24
- package/dist/controllers/menubar_controller.js.map +1 -1
- package/dist/controllers/meter_controller.js +9 -5
- package/dist/controllers/meter_controller.js.map +1 -1
- package/dist/controllers/multi_select_controller.d.ts +18 -3
- package/dist/controllers/multi_select_controller.js +278 -104
- package/dist/controllers/multi_select_controller.js.map +1 -1
- package/dist/controllers/navigation_menu_controller.d.ts +11 -0
- package/dist/controllers/navigation_menu_controller.js +48 -15
- package/dist/controllers/navigation_menu_controller.js.map +1 -1
- package/dist/controllers/nested_form_controller.js +37 -8
- package/dist/controllers/nested_form_controller.js.map +1 -1
- package/dist/controllers/network_status_controller.js +9 -1
- package/dist/controllers/network_status_controller.js.map +1 -1
- package/dist/controllers/number_input_controller.d.ts +36 -8
- package/dist/controllers/number_input_controller.js +124 -21
- package/dist/controllers/number_input_controller.js.map +1 -1
- package/dist/controllers/optimistic_controller.js +14 -1
- package/dist/controllers/optimistic_controller.js.map +1 -1
- package/dist/controllers/otp_controller.d.ts +22 -7
- package/dist/controllers/otp_controller.js +198 -55
- package/dist/controllers/otp_controller.js.map +1 -1
- package/dist/controllers/overflow_indicator_controller.d.ts +8 -12
- package/dist/controllers/overflow_indicator_controller.js +84 -13
- package/dist/controllers/overflow_indicator_controller.js.map +1 -1
- package/dist/controllers/overflow_menu_controller.d.ts +26 -6
- package/dist/controllers/overflow_menu_controller.js +112 -41
- package/dist/controllers/overflow_menu_controller.js.map +1 -1
- package/dist/controllers/pagination_controller.d.ts +19 -11
- package/dist/controllers/pagination_controller.js +74 -28
- package/dist/controllers/pagination_controller.js.map +1 -1
- package/dist/controllers/password_reveal_controller.d.ts +15 -1
- package/dist/controllers/password_reveal_controller.js +59 -2
- package/dist/controllers/password_reveal_controller.js.map +1 -1
- package/dist/controllers/persist_controller.js +30 -8
- package/dist/controllers/persist_controller.js.map +1 -1
- package/dist/controllers/pointer_drag_controller.js +131 -52
- package/dist/controllers/pointer_drag_controller.js.map +1 -1
- package/dist/controllers/popover_controller.d.ts +9 -3
- package/dist/controllers/popover_controller.js +45 -11
- package/dist/controllers/popover_controller.js.map +1 -1
- package/dist/controllers/portal_controller.d.ts +1 -1
- package/dist/controllers/portal_controller.js +6 -2
- package/dist/controllers/portal_controller.js.map +1 -1
- package/dist/controllers/preview_guard_controller.js +16 -1
- package/dist/controllers/preview_guard_controller.js.map +1 -1
- package/dist/controllers/progress_controller.js +8 -4
- package/dist/controllers/progress_controller.js.map +1 -1
- package/dist/controllers/radio_group_controller.d.ts +6 -4
- package/dist/controllers/radio_group_controller.js +42 -17
- package/dist/controllers/radio_group_controller.js.map +1 -1
- package/dist/controllers/range_slider_controller.d.ts +49 -1
- package/dist/controllers/range_slider_controller.js +88 -42
- package/dist/controllers/range_slider_controller.js.map +1 -1
- package/dist/controllers/rating_controller.d.ts +14 -3
- package/dist/controllers/rating_controller.js +39 -15
- package/dist/controllers/rating_controller.js.map +1 -1
- package/dist/controllers/read_more_controller.d.ts +24 -2
- package/dist/controllers/read_more_controller.js +69 -2
- package/dist/controllers/read_more_controller.js.map +1 -1
- package/dist/controllers/resizable_controller.d.ts +16 -2
- package/dist/controllers/resizable_controller.js +82 -22
- package/dist/controllers/resizable_controller.js.map +1 -1
- package/dist/controllers/scroll_area_controller.js +5 -1
- package/dist/controllers/scroll_area_controller.js.map +1 -1
- package/dist/controllers/scroll_visibility_controller.js +0 -1
- package/dist/controllers/scroll_visibility_controller.js.map +1 -1
- package/dist/controllers/scrollspy_controller.d.ts +3 -2
- package/dist/controllers/scrollspy_controller.js +6 -0
- package/dist/controllers/scrollspy_controller.js.map +1 -1
- package/dist/controllers/separator_controller.d.ts +41 -14
- package/dist/controllers/separator_controller.js +66 -37
- package/dist/controllers/separator_controller.js.map +1 -1
- package/dist/controllers/sidebar_controller.d.ts +20 -3
- package/dist/controllers/sidebar_controller.js +77 -18
- package/dist/controllers/sidebar_controller.js.map +1 -1
- package/dist/controllers/skeleton_controller.js +6 -1
- package/dist/controllers/skeleton_controller.js.map +1 -1
- package/dist/controllers/slider_controller.d.ts +45 -7
- package/dist/controllers/slider_controller.js +82 -47
- package/dist/controllers/slider_controller.js.map +1 -1
- package/dist/controllers/smart_sticky_header_controller.js +11 -1
- package/dist/controllers/smart_sticky_header_controller.js.map +1 -1
- package/dist/controllers/sortable_controller.js +17 -2
- package/dist/controllers/sortable_controller.js.map +1 -1
- package/dist/controllers/spinner_controller.js +10 -2
- package/dist/controllers/spinner_controller.js.map +1 -1
- package/dist/controllers/step_indicator_controller.d.ts +19 -17
- package/dist/controllers/step_indicator_controller.js +18 -17
- package/dist/controllers/step_indicator_controller.js.map +1 -1
- package/dist/controllers/stepper_controller.d.ts +34 -9
- package/dist/controllers/stepper_controller.js +101 -19
- package/dist/controllers/stepper_controller.js.map +1 -1
- package/dist/controllers/stick_to_bottom_controller.d.ts +19 -0
- package/dist/controllers/stick_to_bottom_controller.js +75 -5
- package/dist/controllers/stick_to_bottom_controller.js.map +1 -1
- package/dist/controllers/submit_once_controller.d.ts +3 -2
- package/dist/controllers/submit_once_controller.js +16 -3
- package/dist/controllers/submit_once_controller.js.map +1 -1
- package/dist/controllers/switch_controller.d.ts +29 -7
- package/dist/controllers/switch_controller.js +101 -10
- package/dist/controllers/switch_controller.js.map +1 -1
- package/dist/controllers/tabs_controller.d.ts +12 -0
- package/dist/controllers/tabs_controller.js +21 -2
- package/dist/controllers/tabs_controller.js.map +1 -1
- package/dist/controllers/tags_input_controller.d.ts +15 -3
- package/dist/controllers/tags_input_controller.js +209 -59
- package/dist/controllers/tags_input_controller.js.map +1 -1
- package/dist/controllers/theme_controller.d.ts +20 -3
- package/dist/controllers/theme_controller.js +64 -14
- package/dist/controllers/theme_controller.js.map +1 -1
- package/dist/controllers/time_picker_controller.js +23 -8
- package/dist/controllers/time_picker_controller.js.map +1 -1
- package/dist/controllers/toast_controller.d.ts +52 -15
- package/dist/controllers/toast_controller.js +337 -54
- package/dist/controllers/toast_controller.js.map +1 -1
- package/dist/controllers/toggle_group_controller.d.ts +49 -7
- package/dist/controllers/toggle_group_controller.js +159 -23
- package/dist/controllers/toggle_group_controller.js.map +1 -1
- package/dist/controllers/toolbar_controller.js +32 -0
- package/dist/controllers/toolbar_controller.js.map +1 -1
- package/dist/controllers/tooltip_controller.d.ts +8 -0
- package/dist/controllers/tooltip_controller.js +39 -13
- package/dist/controllers/tooltip_controller.js.map +1 -1
- package/dist/controllers/transition_controller.js +4 -0
- package/dist/controllers/transition_controller.js.map +1 -1
- package/dist/controllers/tree_view_controller.d.ts +39 -8
- package/dist/controllers/tree_view_controller.js +169 -16
- package/dist/controllers/tree_view_controller.js.map +1 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +4588 -1535
- package/dist/index.js.map +1 -1
- package/dist/inspector/cli.d.ts +72 -6
- package/dist/inspector/cli.js +262 -51
- package/dist/inspector/cli.js.map +1 -1
- package/dist/inspector/cli_bin.js +309 -51
- package/dist/inspector/cli_bin.js.map +1 -1
- package/dist/inspector/examples.json +40 -40
- package/dist/inspector/manifest.json +525 -59
- package/dist/positioning/index.js +2 -0
- package/dist/positioning/index.js.map +1 -1
- package/package.json +2 -2
|
@@ -127,7 +127,20 @@ function parseStringList(raw, fallback = []) {
|
|
|
127
127
|
return parsed.filter((entry) => typeof entry === "string");
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
// src/utils/transient_hooks.ts
|
|
131
|
+
var TransientHooks = class {
|
|
132
|
+
#attributes;
|
|
133
|
+
constructor(options) {
|
|
134
|
+
this.#attributes = options.attributes;
|
|
135
|
+
}
|
|
136
|
+
/** Drops the declared hooks from `element`; what it carries besides stays. */
|
|
137
|
+
reset(element) {
|
|
138
|
+
for (const attribute of this.#attributes) element.removeAttribute(attribute);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
130
142
|
// src/controllers/persist_controller.ts
|
|
143
|
+
var TRANSIENT = new TransientHooks({ attributes: ["data-persist-restored"] });
|
|
131
144
|
var NON_VALUE_TYPES = /* @__PURE__ */ new Set(["file", "submit", "reset", "button", "image"]);
|
|
132
145
|
var SENSITIVE_TYPES = /* @__PURE__ */ new Set(["password"]);
|
|
133
146
|
var DEFAULT_EXCLUDE = ["authenticity_token", "_method", "utf8"];
|
|
@@ -197,7 +210,6 @@ var PersistController = class extends Controller {
|
|
|
197
210
|
this.#excluded = parseStringList(this.excludeValue, DEFAULT_EXCLUDE);
|
|
198
211
|
this.#knownFields = /* @__PURE__ */ new WeakSet();
|
|
199
212
|
this.#forcedRestore.clear();
|
|
200
|
-
this.element.removeAttribute("data-persist-restored");
|
|
201
213
|
this.element.addEventListener("input", this.#onInput);
|
|
202
214
|
this.element.addEventListener("change", this.#onInput);
|
|
203
215
|
this.#syncClearOn();
|
|
@@ -207,7 +219,7 @@ var PersistController = class extends Controller {
|
|
|
207
219
|
attributeFilter: [
|
|
208
220
|
"checked",
|
|
209
221
|
"data-controller",
|
|
210
|
-
|
|
222
|
+
`data-${this.identifier}-target`,
|
|
211
223
|
"id",
|
|
212
224
|
"multiple",
|
|
213
225
|
"name",
|
|
@@ -230,7 +242,7 @@ var PersistController = class extends Controller {
|
|
|
230
242
|
this.#restoreDynamic.cancel();
|
|
231
243
|
this.#forcedRestore.clear();
|
|
232
244
|
this.#timeouts.clearAll();
|
|
233
|
-
this.element
|
|
245
|
+
TRANSIENT.reset(this.element);
|
|
234
246
|
this.#payload = null;
|
|
235
247
|
}
|
|
236
248
|
/** Drops the active draft after storage confirms the removal. */
|
|
@@ -329,7 +341,7 @@ var PersistController = class extends Controller {
|
|
|
329
341
|
}
|
|
330
342
|
/** Reads, validates, and restores the active namespace. */
|
|
331
343
|
#loadActiveDraft() {
|
|
332
|
-
this.element
|
|
344
|
+
TRANSIENT.reset(this.element);
|
|
333
345
|
this.#payload = null;
|
|
334
346
|
this.#knownFields = /* @__PURE__ */ new WeakSet();
|
|
335
347
|
this.#forcedRestore.clear();
|
|
@@ -481,7 +493,7 @@ var PersistController = class extends Controller {
|
|
|
481
493
|
}
|
|
482
494
|
/** Whether this instance, rather than a nested Persist host, owns a field. */
|
|
483
495
|
#ownsField(field) {
|
|
484
|
-
return field.closest(
|
|
496
|
+
return field.closest(`[data-controller~="${this.identifier}"]`) === this.element;
|
|
485
497
|
}
|
|
486
498
|
/** Marks all currently eligible fields without applying a payload. */
|
|
487
499
|
#markCurrentFieldsKnown() {
|
|
@@ -515,7 +527,12 @@ var PersistController = class extends Controller {
|
|
|
515
527
|
const name = value.trim();
|
|
516
528
|
return name.length > 0 && !/\s/.test(name) ? name : null;
|
|
517
529
|
}
|
|
518
|
-
/**
|
|
530
|
+
/**
|
|
531
|
+
* Collects dynamic controls and select-option changes into one restore pass.
|
|
532
|
+
*
|
|
533
|
+
* @stimeoRuntimeOnly `key` decides whether an id change moves the storage namespace; it decides
|
|
534
|
+
* nothing shown.
|
|
535
|
+
*/
|
|
519
536
|
#onMutations(records) {
|
|
520
537
|
let rootIdChanged = false;
|
|
521
538
|
for (const record of records) {
|
|
@@ -563,7 +580,12 @@ var PersistController = class extends Controller {
|
|
|
563
580
|
this.#forcedRestore.add(field);
|
|
564
581
|
}
|
|
565
582
|
}
|
|
566
|
-
/**
|
|
583
|
+
/**
|
|
584
|
+
* Resolves the logical key from the Value, falling back to the host id.
|
|
585
|
+
*
|
|
586
|
+
* @stimeoRuntimeOnly `key` names the storage namespace; the restored fields come from what is
|
|
587
|
+
* stored there.
|
|
588
|
+
*/
|
|
567
589
|
#resolveLogicalKey() {
|
|
568
590
|
const key = this.keyValue || this.element.id;
|
|
569
591
|
return key.length > 0 ? key : null;
|
|
@@ -572,7 +594,7 @@ var PersistController = class extends Controller {
|
|
|
572
594
|
#storageKey(logicalKey) {
|
|
573
595
|
return `${STORAGE_PREFIX}${logicalKey}`;
|
|
574
596
|
}
|
|
575
|
-
/** Normalizes invalid debounce
|
|
597
|
+
/** Normalizes an invalid `debounce` Value to the `debounce` default. */
|
|
576
598
|
get #debounceDelay() {
|
|
577
599
|
const value = this.debounceValue;
|
|
578
600
|
return Number.isFinite(value) && value >= 0 ? value : DEFAULT_DEBOUNCE;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/microtask_coalescer.ts","../../src/utils/safe_storage.ts","../../src/utils/safe_timeout.ts","../../src/utils/string_list.ts","../../src/controllers/persist_controller.ts"],"names":[],"mappings":"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","file":"persist_controller.js","sourcesContent":["/**\n * Collapses many Stimulus lifecycle callbacks from one DOM mutation into a\n * single pass.\n *\n * Stimulus fires `<name>TargetConnected` / `Disconnected` once per element and\n * `<name>ValueChanged` once per changed attribute. Replacing a list of N options\n * or morphing several render Values therefore delivers N callbacks — but the\n * useful unit of work is \"reconcile against the resulting declarative input\",\n * once, after the batch has settled. Every controller with reconcilable targets\n * or render Values needs the same shape: a `queued` flag plus `queueMicrotask`.\n *\n * **A microtask is the right horizon, and the reason is specific.** Stimulus\n * drives these callbacks from a `MutationObserver`, whose own callback already\n * runs as a microtask with the whole batch in hand; scheduling one more lands\n * after the last sibling callback of that batch and still before paint or any\n * event handler. A timer would be later than it needs to be, and reconciling\n * synchronously would run once per element against a half-applied DOM.\n *\n * **The two guards are not the same guard.** Scheduling is refused before the\n * controller connects, and running is refused after it disconnects:\n *\n * - **Before `connect()`** — Stimulus delivers initial target and Value callbacks\n * ahead of `connect()`. Reconciling there would compute output against a\n * controller whose own state has not been initialised, and `connect()` is\n * about to do a full pass anyway.\n * - **After `disconnect()`** — Stimulus fires a callback for **every** target\n * during teardown, and a microtask queued just before it would otherwise run\n * against a detached tree. {@link MicrotaskCoalescer.cancel} exists for the\n * teardown path to drop the pending pass outright.\n *\n * Both guards are part of one contract here rather than something each consumer\n * has to remember separately.\n *\n * Scope is the scheduling only. *What* to reconcile — keep the surviving active\n * option, fall back to the next / previous / first visible one, rebuild derived\n * chips or hidden fields — stays in the controller, because no two consumers\n * answer it the same way.\n *\n * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and only the contract belongs on the members.\n *\n * @example\n * ```ts\n * readonly #reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());\n *\n * connect() { this.#reconcile.activate(); }\n * disconnect() { this.#reconcile.cancel(); }\n *\n * optionTargetConnected() { this.#reconcile.schedule(); }\n * optionTargetDisconnected() { this.#reconcile.schedule(); }\n * ```\n */\nexport class MicrotaskCoalescer {\n readonly #run: () => void;\n #queued = false;\n #active = false;\n #generation = 0;\n\n /** @param run - the single reconciliation pass, invoked at most once per batch. */\n constructor(run: () => void) {\n this.#run = run;\n }\n\n /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */\n activate(): void {\n this.#active = true;\n }\n\n /** Closes the window and drops any pending pass; call from `disconnect()`. */\n cancel(): void {\n this.#active = false;\n this.#queued = false;\n this.#generation += 1;\n }\n\n /** Requests one pass after the batch settles. Idempotent; inert outside the window. */\n schedule(): void {\n if (!this.#active || this.#queued) return;\n this.#queued = true;\n const generation = this.#generation;\n queueMicrotask(() => {\n // A cancelled callback must not consume a pass queued after reconnect.\n if (generation !== this.#generation || !this.#queued || !this.#active) return;\n this.#queued = false;\n this.#run();\n });\n }\n}\n","/** A successful guarded storage operation. */\nexport interface StorageSuccess<T> {\n readonly ok: true;\n readonly value: T;\n}\n\n/** A guarded storage operation rejected by the browser. */\nexport interface StorageFailure {\n readonly ok: false;\n readonly error: unknown;\n}\n\n/** Result of accessing browser storage without allowing an exception to escape. */\nexport type StorageResult<T> = StorageSuccess<T> | StorageFailure;\n\n/**\n * Guarded `localStorage` access shared by controllers that persist client state.\n *\n * `window.localStorage` can throw while resolving the storage object or while\n * reading, writing, or removing a key. The result keeps an unavailable store\n * distinct from an available store whose key is unset, so callers can expose an\n * accurate success or failure state without duplicating exception guards.\n */\n\n/**\n * Reads `key` from `localStorage`.\n *\n * @param key - The storage key to read.\n * @returns A successful result containing the stored string or `null`, or a\n * failure result when storage is unavailable.\n */\nexport function readLocalStorage(key: string): StorageResult<string | null> {\n try {\n return { ok: true, value: window.localStorage.getItem(key) };\n } catch (error: unknown) {\n return { ok: false, error };\n }\n}\n\n/**\n * Writes `value` under `key` in `localStorage`.\n *\n * @param key - The storage key to write.\n * @param value - The string to store.\n * @returns Success only after `setItem` completes; otherwise the browser error.\n */\nexport function writeLocalStorage(key: string, value: string): StorageResult<void> {\n try {\n window.localStorage.setItem(key, value);\n return { ok: true, value: undefined };\n } catch (error: unknown) {\n return { ok: false, error };\n }\n}\n\n/**\n * Removes `key` from `localStorage`.\n *\n * @param key - The storage key to remove.\n * @returns Success only after `removeItem` completes; otherwise the browser error.\n */\nexport function removeLocalStorage(key: string): StorageResult<void> {\n try {\n window.localStorage.removeItem(key);\n return { ok: true, value: undefined };\n } catch (error: unknown) {\n return { ok: false, error };\n }\n}\n","/**\n * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. Higher-level policy (pause/resume, remaining-time accounting)\n * stays in the individual controllers so per-widget semantics are not flattened\n * into a lowest-common-denominator helper.\n */\n\n/**\n * Largest delay a timer can hold: the platform stores it in a 32-bit signed\n * integer, and anything larger overflows to `1`, so a delay meant to be far in\n * the future fires almost immediately. A declared delay above this bound names\n * no delay at all, and a controller reading one falls back to its default.\n */\nexport const MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","/**\n * JSON string-list parsing for the controllers that take a list as a Value.\n *\n * Stimulus offers an `Array` Value type, but its reader runs `JSON.parse` inside\n * the value observer — **before** the `<name>ValueChanged` callback — and\n * rethrows on malformed text. The throw propagates out of the observer that runs\n * during connection, so a single unparseable attribute stops the controller from\n * connecting at all: no lifecycle callback runs and the element is left inert\n * rather than degraded. A consumer cannot guard against it either, because the\n * callback that would hold the `try` never runs.\n *\n * Declaring the attribute as a `String` Value and parsing it here keeps a\n * malformed declaration local to the value it declares. The attribute text is\n * identical either way (`'[\"a\", \"b\"]'`), so markup does not change with the\n * declaration.\n */\n\n/**\n * Parses `raw` as a JSON array of strings.\n *\n * Returns `fallback` when the text is absent, unparseable, or not an array, so a\n * malformed declaration behaves like an omitted one. A parsed array keeps only\n * its string entries: every caller indexes or compares strings, and a stray\n * number would otherwise sit in the list without ever matching.\n *\n * An explicit `\"[]\"` is honoured as an empty list rather than falling back — it\n * is the only way to declare \"none\" against a non-empty default.\n *\n * @param raw - the attribute text, e.g. `'[\"password\"]'`.\n * @param fallback - the list to use when `raw` declares nothing usable.\n */\nexport function parseStringList(raw: string, fallback: readonly string[] = []): string[] {\n // The common case is an undeclared attribute, and `JSON.parse(\"\")` answers it\n // by throwing. Reaching the same fallback without building an exception keeps\n // the callers that ask per element off that path.\n const text = raw.trim();\n if (text.length === 0) return [...fallback];\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return [...fallback];\n }\n\n if (!Array.isArray(parsed)) return [...fallback];\n return parsed.filter((entry): entry is string => typeof entry === \"string\");\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { MicrotaskCoalescer } from \"../utils/microtask_coalescer\";\nimport { readLocalStorage, removeLocalStorage, writeLocalStorage } from \"../utils/safe_storage\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\nimport { parseStringList } from \"../utils/string_list\";\n\n/** Field controls this controller can persist. */\ntype PersistField = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n\n/** Values that can be restored without coercing an untrusted storage payload. */\ntype PersistValue = string | boolean | string[];\n\n/** One stable field entry in the versioned storage payload. */\ninterface PersistPayloadField {\n readonly key: string;\n readonly value: PersistValue;\n}\n\n/** Versioned draft payload stored under one controller namespace. */\ninterface PersistPayload {\n readonly version: 1;\n readonly fields: PersistPayloadField[];\n}\n\ntype PersistOperation = \"read\" | \"write\" | \"remove\";\ntype PersistErrorReason = \"unavailable\" | \"invalid-payload\";\n\n/** One scheduled write, including the namespace it was created for. */\ninterface PendingSave {\n readonly id: number;\n readonly logicalKey: string;\n}\n\n/** Input types that carry no meaningful, restorable value. */\nconst NON_VALUE_TYPES = new Set([\"file\", \"submit\", \"reset\", \"button\", \"image\"]);\n/** Passwords are never persisted, even when a custom exclusion list is supplied. */\nconst SENSITIVE_TYPES = new Set([\"password\"]);\n/** Field types and names skipped by the default configuration. */\nconst DEFAULT_EXCLUDE = [\"authenticity_token\", \"_method\", \"utf8\"];\n/** localStorage key prefix so drafts never clobber unrelated app storage. */\nconst STORAGE_PREFIX = \"stimeo--persist:\";\n/** Separator for disambiguating repeated same-name fields. */\nconst OCCURRENCE_SEP = \"\\u0000\";\n/** The one observed attribute that can move fields between nested Persist hosts. */\nconst OWNERSHIP_ATTRIBUTE = \"data-controller\";\nconst DEFAULT_DEBOUNCE = 400;\n\n/** Whether a parsed JSON value is a plain object-like record. */\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** Whether a parsed JSON value belongs to the persisted field-value union. */\nconst isPersistValue = (value: unknown): value is PersistValue =>\n typeof value === \"string\" ||\n typeof value === \"boolean\" ||\n (Array.isArray(value) && value.every((item) => typeof item === \"string\"));\n\n/** Parses and validates the complete versioned payload without type assertions. */\nconst parsePayload = (raw: string): PersistPayload | null => {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!isRecord(parsed) || parsed.version !== 1 || !Array.isArray(parsed.fields)) return null;\n\n const fields: PersistPayloadField[] = [];\n const keys = new Set<string>();\n for (const candidate of parsed.fields) {\n if (!isRecord(candidate)) return null;\n const { key, value } = candidate;\n if (typeof key !== \"string\" || key.length === 0 || keys.has(key)) return null;\n if (!isPersistValue(value)) return null;\n keys.add(key);\n fields.push({ key, value });\n }\n return { version: 1, fields };\n};\n\n/** Whether a node is one of the native controls supported by Persist. */\nconst isPersistField = (node: unknown): node is PersistField =>\n node instanceof HTMLInputElement ||\n node instanceof HTMLTextAreaElement ||\n node instanceof HTMLSelectElement;\n\n/**\n * Headless draft-autosave behavior: persists form controls to `localStorage`\n * and restores them across Turbo navigation and reloads.\n *\n * Markup contract (identifier: `stimeo--persist`):\n * <form data-controller=\"stimeo--persist\"\n * data-stimeo--persist-key-value=\"contact-draft\">\n * <input name=\"name\">\n * <textarea name=\"message\"></textarea>\n * </form>\n *\n * The optional `field` targets restrict persistence to explicitly targeted\n * controls. The public `clear` action removes the active draft. `key`,\n * `debounce`, `exclude`, and `clearOn` Values can change while connected;\n * `clearOn` is an event name whose occurrence invokes `clear`.\n *\n * `restore`, `save`, and `clear` dispatch `{ key: string }`. `error` dispatches\n * `{ key: string, operation: \"read\" | \"write\" | \"remove\", reason:\n * \"unavailable\" | \"invalid-payload\" }`. Restore writes are intentionally\n * silent native-control mutations; consumers synchronize from the bubbling\n * `restore` event without treating a restored draft as user input.\n *\n * @remarks\n * Behavior only — restoration never moves focus. Passwords are never stored;\n * the default exclusion also omits Rails request metadata while preserving\n * application-owned hidden fields. Distinct logical keys isolate instances;\n * instances using the same key intentionally share one last-writer-wins draft.\n * Every listener, timer, observer, and queued reconciliation is released on\n * `disconnect()`, after any pending edit is flushed through the normal save\n * path. A reconnect removes stale restored state before reading storage.\n */\nexport class PersistController extends Controller<HTMLElement> {\n static override targets = [\"field\"];\n static override values = {\n key: { type: String, default: \"\" },\n debounce: { type: Number, default: DEFAULT_DEBOUNCE },\n // A malformed JSON list must fall back without aborting Stimulus connection.\n exclude: { type: String, default: \"\" },\n clearOn: { type: String, default: \"\" },\n };\n static actions = [\"clear\"] as const;\n static events = [\"restore\", \"save\", \"clear\", \"error\"] as const;\n\n declare readonly fieldTargets: Element[];\n declare readonly hasFieldTarget: boolean;\n\n declare keyValue: string;\n declare debounceValue: number;\n declare excludeValue: string;\n declare clearOnValue: string;\n\n readonly #timeouts = new SafeTimeout();\n readonly #restoreDynamic = new MicrotaskCoalescer(() => this.#restoreDynamicFields());\n readonly #observer = new MutationObserver((records) => this.#onMutations(records));\n #pendingSave: PendingSave | null = null;\n #connected = false;\n #logicalKey: string | null = null;\n #payload: PersistPayload | null = null;\n #registeredClearOn: string | null = null;\n #excluded: readonly string[] = DEFAULT_EXCLUDE;\n #knownFields = new WeakSet<PersistField>();\n #forcedRestore = new Set<PersistField>();\n\n readonly #onInput = (event: Event): void => {\n const field = event.target;\n if (!isPersistField(field) || !this.#ownsField(field)) return;\n if (this.hasFieldTarget && !this.fieldTargets.includes(field)) return;\n if (this.#keyOf(field) === null || !this.#persistable(field, this.#excluded)) return;\n this.#scheduleSave();\n };\n\n readonly #onClearEvent = (): void => {\n this.clear();\n };\n\n override connect(): void {\n if (this.#connected) return;\n this.#connected = true;\n this.#logicalKey = this.#resolveLogicalKey();\n this.#excluded = parseStringList(this.excludeValue, DEFAULT_EXCLUDE);\n this.#knownFields = new WeakSet<PersistField>();\n this.#forcedRestore.clear();\n this.element.removeAttribute(\"data-persist-restored\");\n\n this.element.addEventListener(\"input\", this.#onInput);\n this.element.addEventListener(\"change\", this.#onInput);\n this.#syncClearOn();\n this.#restoreDynamic.activate();\n this.#observer.observe(this.element, {\n attributes: true,\n attributeFilter: [\n \"checked\",\n \"data-controller\",\n \"data-stimeo--persist-target\",\n \"id\",\n \"multiple\",\n \"name\",\n \"selected\",\n \"type\",\n \"value\",\n ],\n childList: true,\n subtree: true,\n });\n this.#loadActiveDraft();\n }\n\n override disconnect(): void {\n this.#flushPendingSave();\n this.#connected = false;\n this.element.removeEventListener(\"input\", this.#onInput);\n this.element.removeEventListener(\"change\", this.#onInput);\n this.#unbindClearOn();\n this.#observer.disconnect();\n this.#restoreDynamic.cancel();\n this.#forcedRestore.clear();\n this.#timeouts.clearAll();\n this.element.removeAttribute(\"data-persist-restored\");\n this.#payload = null;\n }\n\n /** Drops the active draft after storage confirms the removal. */\n clear(): void {\n const logicalKey = this.#logicalKey ?? this.#resolveLogicalKey();\n if (logicalKey === null) return;\n this.#cancelPendingSave();\n const result = removeLocalStorage(this.#storageKey(logicalKey));\n if (!result.ok) {\n this.#dispatchError(logicalKey, \"remove\", \"unavailable\");\n return;\n }\n this.#payload = null;\n this.element.removeAttribute(\"data-persist-restored\");\n this.dispatch(\"clear\", { detail: { key: logicalKey } });\n }\n\n /** Rebinds the external clear event when its Value changes. */\n clearOnValueChanged(): void {\n if (this.#connected) this.#syncClearOn();\n }\n\n /** Switches storage namespaces without writing old edits under the new key. */\n keyValueChanged(): void {\n if (this.#connected) this.#switchLogicalKey();\n }\n\n /** Reschedules a pending write against the current debounce delay. */\n debounceValueChanged(): void {\n if (this.#connected && this.#pendingSave !== null) this.#scheduleSave();\n }\n\n /** Restores every field the new exclusion list makes eligible. */\n excludeValueChanged(): void {\n const previous = this.#excluded;\n this.#excluded = parseStringList(this.excludeValue, DEFAULT_EXCLUDE);\n if (!this.#connected) return;\n // A field restored once stays in #knownFields, so releasing it from the exclusion\n // list has to name it explicitly to reach the next batch. Fields the list never\n // covered keep their live value, which is what the next batch already guarantees.\n for (const field of this.#candidateFields()) {\n if (this.#persistable(field, this.#excluded) && !this.#persistable(field, previous)) {\n this.#forcedRestore.add(field);\n }\n }\n this.#restoreDynamic.schedule();\n }\n\n /** Schedules a debounced save. */\n #scheduleSave(): void {\n const logicalKey = this.#logicalKey;\n if (logicalKey === null) return;\n this.#cancelPendingSave();\n const id = this.#timeouts.set(() => {\n this.#pendingSave = null;\n this.#save(logicalKey);\n }, this.#debounceDelay);\n this.#pendingSave = { id, logicalKey };\n }\n\n /** Cancels the currently pending save, if any. */\n #cancelPendingSave(): void {\n const pending = this.#pendingSave;\n if (pending === null) return;\n this.#timeouts.clear(pending.id);\n this.#pendingSave = null;\n }\n\n /** Flushes a pending edit through the same success/error event path as a timer. */\n #flushPendingSave(): void {\n const pending = this.#pendingSave;\n if (pending === null) return;\n this.#cancelPendingSave();\n this.#save(pending.logicalKey);\n }\n\n /** Writes the active payload and emits `save` only after storage succeeds. */\n #save(logicalKey: string): boolean {\n const payload = this.#serialize();\n const result = writeLocalStorage(this.#storageKey(logicalKey), JSON.stringify(payload));\n if (!result.ok) {\n this.#dispatchError(logicalKey, \"write\", \"unavailable\");\n return false;\n }\n this.#payload = payload;\n this.dispatch(\"save\", { detail: { key: logicalKey } });\n return true;\n }\n\n /** Serializes every persistable field into a versioned, typed payload. */\n #serialize(): PersistPayload {\n const fields: PersistPayloadField[] = [];\n for (const { field, key } of this.#fieldEntries()) {\n if (field instanceof HTMLInputElement && field.type === \"checkbox\") {\n fields.push({ key, value: field.checked });\n } else if (field instanceof HTMLInputElement && field.type === \"radio\") {\n if (field.checked) fields.push({ key, value: field.value });\n } else if (field instanceof HTMLSelectElement && field.multiple) {\n fields.push({\n key,\n value: Array.from(field.selectedOptions).map((option) => option.value),\n });\n } else {\n fields.push({ key, value: field.value });\n }\n }\n return { version: 1, fields };\n }\n\n /** Reads, validates, and restores the active namespace. */\n #loadActiveDraft(): void {\n this.element.removeAttribute(\"data-persist-restored\");\n this.#payload = null;\n this.#knownFields = new WeakSet<PersistField>();\n this.#forcedRestore.clear();\n const logicalKey = this.#logicalKey;\n if (logicalKey === null) {\n this.#markCurrentFieldsKnown();\n return;\n }\n\n const result = readLocalStorage(this.#storageKey(logicalKey));\n if (!result.ok) {\n this.#dispatchError(logicalKey, \"read\", \"unavailable\");\n this.#markCurrentFieldsKnown();\n return;\n }\n if (result.value === null) {\n this.#markCurrentFieldsKnown();\n return;\n }\n\n const payload = parsePayload(result.value);\n if (payload === null) {\n const removal = removeLocalStorage(this.#storageKey(logicalKey));\n // Cleanup precedes notification so an error consumer can safely replace\n // the invalid value without that repair being deleted on callback return.\n this.#dispatchError(logicalKey, \"read\", \"invalid-payload\");\n if (!removal.ok) this.#dispatchError(logicalKey, \"remove\", \"unavailable\");\n this.#markCurrentFieldsKnown();\n return;\n }\n this.#payload = payload;\n this.#restoreEntries(null);\n }\n\n /** Restores fields inserted or made eligible after the initial connection. */\n #restoreDynamicFields(): void {\n const selected = new Set<PersistField>(this.#forcedRestore);\n this.#forcedRestore.clear();\n for (const { field } of this.#fieldEntries()) {\n if (!this.#knownFields.has(field)) selected.add(field);\n }\n this.#restoreEntries(selected);\n }\n\n /** Applies the active payload to all fields, or only the supplied candidates. */\n #restoreEntries(selected: ReadonlySet<PersistField> | null): void {\n const entries = this.#fieldEntries();\n for (const { field } of entries) this.#knownFields.add(field);\n const payload = this.#payload;\n if (payload === null) return;\n const savedFields = new Map(payload.fields.map((field) => [field.key, field]));\n const restoredKeys = new Set<string>();\n const processedRadios = new Set<string>();\n\n for (const entry of entries) {\n if (selected !== null && !selected.has(entry.field)) continue;\n const saved = savedFields.get(entry.key);\n if (saved === undefined) continue;\n const { value } = saved;\n\n if (entry.field instanceof HTMLInputElement && entry.field.type === \"radio\") {\n if (processedRadios.has(entry.key)) continue;\n processedRadios.add(entry.key);\n const radios = entries\n .filter(\n ({ key, field }) =>\n key === entry.key && field instanceof HTMLInputElement && field.type === \"radio\",\n )\n .map(({ field }) => field as HTMLInputElement);\n if (typeof value !== \"string\" || !radios.some((radio) => radio.value === value)) continue;\n for (const radio of radios) radio.checked = radio.value === value;\n restoredKeys.add(entry.key);\n continue;\n }\n\n if (this.#applyValue(entry.field, value)) restoredKeys.add(entry.key);\n }\n\n if (restoredKeys.size > 0) {\n // Property writes can reflect to observed attributes in DOM implementations.\n // Drop only records produced by this restore before notifying consumers, so\n // authored mutations made from a restore listener remain observable.\n this.#observer.takeRecords();\n this.element.setAttribute(\"data-persist-restored\", \"true\");\n this.dispatch(\"restore\", { detail: { key: this.#logicalKey } });\n }\n }\n\n /** Applies a type-compatible stored value without coercion. */\n #applyValue(field: PersistField, value: PersistValue): boolean {\n if (field instanceof HTMLInputElement && field.type === \"checkbox\") {\n if (typeof value !== \"boolean\") return false;\n field.checked = value;\n return true;\n }\n if (field instanceof HTMLSelectElement && field.multiple) {\n if (!Array.isArray(value)) return false;\n const available = new Set(Array.from(field.options).map((option) => option.value));\n const selected = value.filter((candidate) => available.has(candidate));\n if (value.length > 0 && selected.length === 0) return false;\n const selectedValues = new Set(selected);\n for (const option of Array.from(field.options)) {\n option.selected = selectedValues.has(option.value);\n }\n return true;\n }\n if (typeof value !== \"string\") return false;\n if (field instanceof HTMLSelectElement) {\n const exists = Array.from(field.options).some((option) => option.value === value);\n if (!exists) return false;\n }\n field.value = value;\n return true;\n }\n\n /** Every control this instance owns, before the exclusion list narrows them. */\n #candidateFields(): PersistField[] {\n return this.hasFieldTarget\n ? this.fieldTargets.filter(isPersistField)\n : Array.from(this.element.querySelectorAll<PersistField>(\"input, textarea, select\")).filter(\n (field) => this.#ownsField(field),\n );\n }\n\n /**\n * Pairs persistable fields with DOM-order occurrence keys. Every HTML radio\n * group (same name and form owner) occupies one shared occurrence slot.\n */\n #fieldEntries(): Array<{ field: PersistField; key: string }> {\n const entries: Array<{ field: PersistField; key: string }> = [];\n const eligible: Array<{ field: PersistField; name: string }> = [];\n for (const field of this.#candidateFields()) {\n const name = this.#keyOf(field);\n if (name === null || !this.#persistable(field, this.#excluded)) continue;\n eligible.push({ field, name });\n }\n\n const nextOccurrence = new Map<string, number>();\n const radioSlots = new Map<string, Map<HTMLFormElement | null, string>>();\n const allocate = (name: string): string => {\n const occurrence = nextOccurrence.get(name) ?? 0;\n nextOccurrence.set(name, occurrence + 1);\n return occurrence === 0 ? name : `${name}${OCCURRENCE_SEP}${occurrence}`;\n };\n for (const { field, name } of eligible) {\n if (field instanceof HTMLInputElement && field.type === \"radio\") {\n const groups = radioSlots.get(name) ?? new Map<HTMLFormElement | null, string>();\n radioSlots.set(name, groups);\n let key = groups.get(field.form);\n if (key === undefined) {\n key = allocate(name);\n groups.set(field.form, key);\n }\n entries.push({ field, key });\n continue;\n }\n entries.push({ field, key: allocate(name) });\n }\n return entries;\n }\n\n /** Whether a field carries a restorable, non-excluded value. */\n #persistable(field: PersistField, excluded: readonly string[]): boolean {\n if (NON_VALUE_TYPES.has(field.type) || SENSITIVE_TYPES.has(field.type)) return false;\n if (excluded.includes(field.type)) return false;\n if (field.name.length > 0 && excluded.includes(field.name)) return false;\n return true;\n }\n\n /** A stable storage sub-key for a field (its name, else id). */\n #keyOf(field: PersistField): string | null {\n return field.name || field.id || null;\n }\n\n /** Whether this instance, rather than a nested Persist host, owns a field. */\n #ownsField(field: PersistField): boolean {\n return field.closest('[data-controller~=\"stimeo--persist\"]') === this.element;\n }\n\n /** Marks all currently eligible fields without applying a payload. */\n #markCurrentFieldsKnown(): void {\n for (const { field } of this.#fieldEntries()) this.#knownFields.add(field);\n }\n\n /** Switches namespace after flushing the old namespace's pending edit. */\n #switchLogicalKey(): void {\n const next = this.#resolveLogicalKey();\n if (next === this.#logicalKey) return;\n this.#flushPendingSave();\n this.#logicalKey = next;\n this.#loadActiveDraft();\n }\n\n /** Synchronizes the exact event name owned by the `clearOn` Value. */\n #syncClearOn(): void {\n const next = this.#validEventName(this.clearOnValue);\n if (next === this.#registeredClearOn) return;\n this.#unbindClearOn();\n if (next === null) return;\n this.element.addEventListener(next, this.#onClearEvent);\n this.#registeredClearOn = next;\n }\n\n /** Removes the event listener using the name that was actually registered. */\n #unbindClearOn(): void {\n if (this.#registeredClearOn === null) return;\n this.element.removeEventListener(this.#registeredClearOn, this.#onClearEvent);\n this.#registeredClearOn = null;\n }\n\n /** Returns a non-whitespace event type, or null when the hook is disabled. */\n #validEventName(value: string): string | null {\n const name = value.trim();\n return name.length > 0 && !/\\s/.test(name) ? name : null;\n }\n\n /** Collects dynamic controls and select-option changes into one restore pass. */\n #onMutations(records: MutationRecord[]): void {\n let rootIdChanged = false;\n for (const record of records) {\n if (record.type === \"attributes\") {\n if (record.target === this.element && record.attributeName === \"id\") {\n rootIdChanged = true;\n }\n this.#collectAttributeCandidate(record.target, record.attributeName);\n continue;\n }\n for (const node of record.addedNodes) this.#collectRestoreCandidate(node);\n }\n if (rootIdChanged && this.keyValue.length === 0) this.#switchLogicalKey();\n this.#restoreDynamic.schedule();\n }\n\n /**\n * Adds the controls one mutated attribute can affect. Only an ownership change on a\n * descendant moves fields between Persist hosts, so that is the single case worth a\n * subtree sweep; every other observed attribute describes one control, and sweeping\n * from its container would re-apply the stored draft over edits still being debounced.\n */\n #collectAttributeCandidate(target: Node, attributeName: string | null): void {\n if (attributeName === OWNERSHIP_ATTRIBUTE && target !== this.element) {\n this.#collectRestoreCandidate(target);\n return;\n }\n this.#collectControlCandidate(target);\n }\n\n /** Adds a native control, or the select that owns a mutated option. */\n #collectControlCandidate(node: Node): void {\n if (isPersistField(node)) {\n this.#forcedRestore.add(node);\n return;\n }\n if (node instanceof HTMLOptionElement) {\n const select = node.closest(\"select\");\n if (select) this.#forcedRestore.add(select);\n }\n }\n\n /** Adds every control inside an inserted or newly owned subtree. */\n #collectRestoreCandidate(node: Node): void {\n this.#collectControlCandidate(node);\n if (!(node instanceof Element)) return;\n for (const field of node.querySelectorAll<PersistField>(\"input, textarea, select\")) {\n this.#forcedRestore.add(field);\n }\n }\n\n /** Resolves the logical key from the Value, falling back to the host id. */\n #resolveLogicalKey(): string | null {\n const key = this.keyValue || this.element.id;\n return key.length > 0 ? key : null;\n }\n\n /** Namespaces one logical key within localStorage. */\n #storageKey(logicalKey: string): string {\n return `${STORAGE_PREFIX}${logicalKey}`;\n }\n\n /** Normalizes invalid debounce Values to the documented default. */\n get #debounceDelay(): number {\n const value = this.debounceValue;\n return Number.isFinite(value) && value >= 0 ? value : DEFAULT_DEBOUNCE;\n }\n\n /** Dispatches one observable storage failure without exposing browser errors. */\n #dispatchError(key: string, operation: PersistOperation, reason: PersistErrorReason): void {\n this.dispatch(\"error\", { detail: { key, operation, reason } });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/microtask_coalescer.ts","../../src/utils/safe_storage.ts","../../src/utils/safe_timeout.ts","../../src/utils/string_list.ts","../../src/utils/transient_hooks.ts","../../src/controllers/persist_controller.ts"],"names":[],"mappings":"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","file":"persist_controller.js","sourcesContent":["/**\n * Collapses many Stimulus lifecycle callbacks from one DOM mutation into a\n * single pass.\n *\n * Stimulus fires `<name>TargetConnected` / `Disconnected` once per element and\n * `<name>ValueChanged` once per changed attribute. Replacing a list of N options\n * or morphing several render Values therefore delivers N callbacks — but the\n * useful unit of work is \"reconcile against the resulting declarative input\",\n * once, after the batch has settled. Every controller with reconcilable targets\n * or render Values needs the same shape: a `queued` flag plus `queueMicrotask`.\n *\n * **A microtask is the right horizon, and the reason is specific.** Stimulus\n * drives these callbacks from a `MutationObserver`, whose own callback already\n * runs as a microtask with the whole batch in hand; scheduling one more lands\n * after the last sibling callback of that batch and still before paint or any\n * event handler. A timer would be later than it needs to be, and reconciling\n * synchronously would run once per element against a half-applied DOM.\n *\n * **The two guards are not the same guard.** Scheduling is refused before the\n * controller connects, and running is refused after it disconnects:\n *\n * - **Before `connect()`** — Stimulus delivers initial target and Value callbacks\n * ahead of `connect()`. Reconciling there would compute output against a\n * controller whose own state has not been initialised, and `connect()` is\n * about to do a full pass anyway.\n * - **After `disconnect()`** — Stimulus fires a callback for **every** target\n * during teardown, and a microtask queued just before it would otherwise run\n * against a detached tree. {@link MicrotaskCoalescer.cancel} exists for the\n * teardown path to drop the pending pass outright.\n *\n * Both guards are part of one contract here rather than something each consumer\n * has to remember separately.\n *\n * Scope is the scheduling only. *What* to reconcile — keep the surviving active\n * option, fall back to the next / previous / first visible one, rebuild derived\n * chips or hidden fields — stays in the controller, because no two consumers\n * answer it the same way.\n *\n * This file's own doc block is dropped from `dist`, but every member comment is\n * inlined into each consumer entry (`tsup` builds with `splitting: false`), so\n * rationale belongs here and only the contract belongs on the members.\n *\n * @example\n * ```ts\n * readonly #reconcile = new MicrotaskCoalescer(() => this.#reconcileOptions());\n *\n * connect() { this.#reconcile.activate(); }\n * disconnect() { this.#reconcile.cancel(); }\n *\n * optionTargetConnected() { this.#reconcile.schedule(); }\n * optionTargetDisconnected() { this.#reconcile.schedule(); }\n * ```\n */\nexport class MicrotaskCoalescer {\n readonly #run: () => void;\n #queued = false;\n #active = false;\n #generation = 0;\n\n /** @param run - the single reconciliation pass, invoked at most once per batch. */\n constructor(run: () => void) {\n this.#run = run;\n }\n\n /** Opens the window in which {@link schedule} is honoured; call from `connect()`. */\n activate(): void {\n this.#active = true;\n }\n\n /** Closes the window and drops any pending pass; call from `disconnect()`. */\n cancel(): void {\n this.#active = false;\n this.#queued = false;\n this.#generation += 1;\n }\n\n /** Requests one pass after the batch settles. Idempotent; inert outside the window. */\n schedule(): void {\n if (!this.#active || this.#queued) return;\n this.#queued = true;\n const generation = this.#generation;\n queueMicrotask(() => {\n // A cancelled callback must not consume a pass queued after reconnect.\n if (generation !== this.#generation || !this.#queued || !this.#active) return;\n this.#queued = false;\n this.#run();\n });\n }\n}\n","/** A successful guarded storage operation. */\nexport interface StorageSuccess<T> {\n readonly ok: true;\n readonly value: T;\n}\n\n/** A guarded storage operation rejected by the browser. */\nexport interface StorageFailure {\n readonly ok: false;\n readonly error: unknown;\n}\n\n/** Result of accessing browser storage without allowing an exception to escape. */\nexport type StorageResult<T> = StorageSuccess<T> | StorageFailure;\n\n/**\n * Guarded `localStorage` access shared by controllers that persist client state.\n *\n * `window.localStorage` can throw while resolving the storage object or while\n * reading, writing, or removing a key. The result keeps an unavailable store\n * distinct from an available store whose key is unset, so callers can expose an\n * accurate success or failure state without duplicating exception guards.\n */\n\n/**\n * Reads `key` from `localStorage`.\n *\n * @param key - The storage key to read.\n * @returns A successful result containing the stored string or `null`, or a\n * failure result when storage is unavailable.\n */\nexport function readLocalStorage(key: string): StorageResult<string | null> {\n try {\n return { ok: true, value: window.localStorage.getItem(key) };\n } catch (error: unknown) {\n return { ok: false, error };\n }\n}\n\n/**\n * Writes `value` under `key` in `localStorage`.\n *\n * @param key - The storage key to write.\n * @param value - The string to store.\n * @returns Success only after `setItem` completes; otherwise the browser error.\n */\nexport function writeLocalStorage(key: string, value: string): StorageResult<void> {\n try {\n window.localStorage.setItem(key, value);\n return { ok: true, value: undefined };\n } catch (error: unknown) {\n return { ok: false, error };\n }\n}\n\n/**\n * Removes `key` from `localStorage`.\n *\n * @param key - The storage key to remove.\n * @returns Success only after `removeItem` completes; otherwise the browser error.\n */\nexport function removeLocalStorage(key: string): StorageResult<void> {\n try {\n window.localStorage.removeItem(key);\n return { ok: true, value: undefined };\n } catch (error: unknown) {\n return { ok: false, error };\n }\n}\n","/**\n * Self-cleaning timer registries shared by Stimeo controllers.\n *\n * Stimulus controllers frequently schedule `setTimeout` / `setInterval` work\n * (auto-dismiss, debouncing, polling). When the element leaves the DOM — a\n * Turbo Drive navigation, a Turbo Stream replacement, or any `disconnect()` —\n * orphaned timers keep firing against a detached controller, leaking memory and\n * mutating stale state. {@link SafeTimeout} and {@link SafeInterval} track every\n * timer they create so a single {@link TimerRegistry.clearAll | clearAll()} call\n * in `disconnect()` tears them all down.\n *\n * These are intentionally low-level primitives: they own *registration and\n * cleanup only*. Higher-level policy (pause/resume, remaining-time accounting)\n * stays in the individual controllers so per-widget semantics are not flattened\n * into a lowest-common-denominator helper.\n */\n\n/**\n * Largest delay a timer can hold: the platform stores it in a 32-bit signed\n * integer, and anything larger overflows to `1`, so a delay meant to be far in\n * the future fires almost immediately. A declared delay above this bound names\n * no delay at all, and a controller reading one falls back to its default.\n */\nexport const MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/**\n * Shared registry bookkeeping for the timeout/interval variants.\n *\n * Subclasses provide the scheduling primitive ({@link schedule}) and its matching\n * canceller ({@link cancel}); this base owns the set of live ids plus the\n * per-id and bulk teardown shared by both.\n */\nabstract class TimerRegistry {\n /** Live timer ids that have not yet been cleared (or, for timeouts, fired). */\n protected readonly ids = new Set<number>();\n\n /** Schedules the underlying platform timer and returns its id. */\n protected abstract schedule(callback: () => void, delay: number): number;\n\n /** Cancels the underlying platform timer for `id`. */\n protected abstract cancel(id: number): void;\n\n /**\n * Cancels a single tracked timer.\n *\n * No-ops if the id is unknown (already cleared, fired, or never owned by this\n * registry), so callers can clear defensively without guarding.\n */\n clear(id: number): void {\n if (this.ids.delete(id)) {\n this.cancel(id);\n }\n }\n\n /**\n * Cancels every tracked timer. Call this from a controller's `disconnect()`\n * to guarantee no timer outlives the element.\n */\n clearAll(): void {\n for (const id of this.ids) {\n this.cancel(id);\n }\n this.ids.clear();\n }\n\n /** Number of timers currently tracked (pending). */\n get size(): number {\n return this.ids.size;\n }\n}\n\n/**\n * `setTimeout` wrapper that auto-forgets each timer once it fires and supports\n * bulk teardown on disconnect.\n *\n * @example\n * ```ts\n * #timers = new SafeTimeout();\n *\n * connect() {\n * this.#timers.set(() => this.dismiss(), 5000);\n * }\n *\n * disconnect() {\n * this.#timers.clearAll();\n * }\n * ```\n */\nexport class SafeTimeout extends TimerRegistry {\n /**\n * Schedules `callback` after `delay` ms and returns the timer id.\n *\n * The id is removed from the registry automatically when the timeout fires,\n * so {@link TimerRegistry.size | size} reflects only still-pending timers.\n */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(() => {\n this.ids.delete(id);\n callback();\n }, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setTimeout(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearTimeout(id);\n }\n}\n\n/**\n * `setInterval` wrapper that tracks every interval for bulk teardown on\n * disconnect. Unlike {@link SafeTimeout}, intervals are retained until they are\n * explicitly cleared because they fire repeatedly.\n *\n * @example\n * ```ts\n * #intervals = new SafeInterval();\n *\n * connect() {\n * this.#intervals.set(() => this.tick(), 1000);\n * }\n *\n * disconnect() {\n * this.#intervals.clearAll();\n * }\n * ```\n */\nexport class SafeInterval extends TimerRegistry {\n /** Schedules a repeating `callback` every `delay` ms and returns the timer id. */\n set(callback: () => void, delay: number): number {\n const id = this.schedule(callback, delay);\n this.ids.add(id);\n return id;\n }\n\n protected schedule(callback: () => void, delay: number): number {\n return window.setInterval(callback, delay);\n }\n\n protected cancel(id: number): void {\n window.clearInterval(id);\n }\n}\n","/**\n * JSON string-list parsing for the controllers that take a list as a Value.\n *\n * Stimulus offers an `Array` Value type, but its reader runs `JSON.parse` inside\n * the value observer — **before** the `<name>ValueChanged` callback — and\n * rethrows on malformed text. The throw propagates out of the observer that runs\n * during connection, so a single unparseable attribute stops the controller from\n * connecting at all: no lifecycle callback runs and the element is left inert\n * rather than degraded. A consumer cannot guard against it either, because the\n * callback that would hold the `try` never runs.\n *\n * Declaring the attribute as a `String` Value and parsing it here keeps a\n * malformed declaration local to the value it declares. The attribute text is\n * identical either way (`'[\"a\", \"b\"]'`), so markup does not change with the\n * declaration.\n */\n\n/**\n * Parses `raw` as a JSON array of strings.\n *\n * Returns `fallback` when the text is absent, unparseable, or not an array, so a\n * malformed declaration behaves like an omitted one. A parsed array keeps only\n * its string entries: every caller indexes or compares strings, and a stray\n * number would otherwise sit in the list without ever matching.\n *\n * An explicit `\"[]\"` is honoured as an empty list rather than falling back — it\n * is the only way to declare \"none\" against a non-empty default.\n *\n * @param raw - the attribute text, e.g. `'[\"password\"]'`.\n * @param fallback - the list to use when `raw` declares nothing usable.\n */\nexport function parseStringList(raw: string, fallback: readonly string[] = []): string[] {\n // The common case is an undeclared attribute, and `JSON.parse(\"\")` answers it\n // by throwing. Reaching the same fallback without building an exception keeps\n // the callers that ask per element off that path.\n const text = raw.trim();\n if (text.length === 0) return [...fallback];\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch {\n return [...fallback];\n }\n\n if (!Array.isArray(parsed)) return [...fallback];\n return parsed.filter((entry): entry is string => typeof entry === \"string\");\n}\n","/**\n * Drops the transient state hooks a connection inherited, so the cycle it opens\n * starts from a DOM that claims nothing.\n *\n * A transient hook is an attribute a controller writes to say \"this is happening\n * right now\" — a drag in progress, a form with unsaved edits, a refused\n * subscription. Its lifetime belongs to the interaction, not to the element, and\n * the two come apart whenever the element outlives the instance that wrote it: a\n * Turbo cache snapshot taken mid-interaction restores the hook with the markup,\n * and an element re-inserted elsewhere in the page carries it along. Nothing\n * takes such a hook off, because the instance that owned it is gone; consumer\n * CSS then keeps claiming a state no one is in.\n *\n * **`turbo:before-cache` cannot do this job**, which is why the pass belongs in\n * `connect()`: that event fires for a navigation only, so an in-page move — the\n * other half of the population — never reaches it, and a hook that arrived with\n * a moved element would survive. `connect()` covers both, because both end in a\n * connection.\n *\n * Scope is the **drop** only. What the fresh cycle should say instead is the\n * consumer's: a value re-read from the live DOM, a roster refilled by the\n * stream, a measurement retaken from the current scroll position, a slot emptied\n * of the last result. Those answers differ per consumer and none of them is a\n * hook removal, so the pass stops where every consumer agrees.\n *\n * A declaration names one group of hooks and carries no per-instance state, so\n * it belongs at module scope, shared by every instance the page connects. A\n * consumer writes as many as it has groups — hooks it drops under different\n * conditions cannot share one. The exception is a hook whose name is only known\n * at runtime, such as one built from the registered identifier: that\n * declaration is a field, because the name is.\n *\n * **A drop is not always right.** The population splits on one question: can the\n * state a hook describes outlive the instance that wrote it? Where it cannot — a\n * drag ends with the pointer, a subscription dies with its socket — dropping on\n * every connection is correct. Where it can — a submission in flight is not\n * interrupted by an in-page move — only a connection that follows a real restore\n * may drop, and the consumer answers \"did a session survive?\" with the signal it\n * already holds for its own teardown (a live-session field, a `DetachGate`\n * probe). The drop then sits behind that answer, as the second example shows.\n *\n * @example Unconditional — the state cannot outlive the instance.\n * ```ts\n * const TRANSIENT = new TransientHooks({ attributes: [\"data-sortable-dragging\"] });\n *\n * connect(): void {\n * TRANSIENT.reset(this.element);\n * }\n * ```\n *\n * @example Gated — an in-page move must not drop a live state.\n * ```ts\n * const TRANSIENT = new TransientHooks({ attributes: [\"data-optimistic\"] });\n *\n * connect(): void {\n * const restored = !this.#connected && !this.#gate.pending;\n * this.#gate.cancel();\n * this.#connected = true;\n * if (restored) TRANSIENT.reset(this.element);\n * }\n * ```\n */\n\n/** What a consumer declares once about the hooks its interaction owns. */\nexport interface TransientHooksOptions {\n /**\n * The attribute names a connection may find written by an earlier one. Hooks\n * a consumer drops under different conditions belong to different\n * declarations, so each one can be reset on its own.\n */\n readonly attributes: readonly string[];\n}\n\n/**\n * One consumer's declaration of the hooks its interaction owns, dropped from an\n * element by {@link TransientHooks.reset}.\n *\n * Every member comment inside this class body is inlined into each consumer\n * bundle (`tsup` builds with `splitting: false`), so rationale belongs in this\n * file's own block, which `dist` drops, and only the contract belongs inside.\n */\nexport class TransientHooks {\n readonly #attributes: readonly string[];\n\n constructor(options: TransientHooksOptions) {\n this.#attributes = options.attributes;\n }\n\n /** Drops the declared hooks from `element`; what it carries besides stays. */\n reset(element: Element): void {\n for (const attribute of this.#attributes) element.removeAttribute(attribute);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { MicrotaskCoalescer } from \"../utils/microtask_coalescer\";\nimport { readLocalStorage, removeLocalStorage, writeLocalStorage } from \"../utils/safe_storage\";\nimport { SafeTimeout } from \"../utils/safe_timeout\";\nimport { parseStringList } from \"../utils/string_list\";\nimport { TransientHooks } from \"../utils/transient_hooks\";\n\n/** The hook a connection may find written by an earlier, now-gone one. */\nconst TRANSIENT = new TransientHooks({ attributes: [\"data-persist-restored\"] });\n\n/** Field controls this controller can persist. */\ntype PersistField = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement;\n\n/** Values that can be restored without coercing an untrusted storage payload. */\ntype PersistValue = string | boolean | string[];\n\n/** One stable field entry in the versioned storage payload. */\ninterface PersistPayloadField {\n readonly key: string;\n readonly value: PersistValue;\n}\n\n/** Versioned draft payload stored under one controller namespace. */\ninterface PersistPayload {\n readonly version: 1;\n readonly fields: PersistPayloadField[];\n}\n\ntype PersistOperation = \"read\" | \"write\" | \"remove\";\ntype PersistErrorReason = \"unavailable\" | \"invalid-payload\";\n\n/** One scheduled write, including the namespace it was created for. */\ninterface PendingSave {\n readonly id: number;\n readonly logicalKey: string;\n}\n\n/** Input types that carry no meaningful, restorable value. */\nconst NON_VALUE_TYPES = new Set([\"file\", \"submit\", \"reset\", \"button\", \"image\"]);\n/** Passwords are never persisted, even when a custom exclusion list is supplied. */\nconst SENSITIVE_TYPES = new Set([\"password\"]);\n/** Field types and names skipped by the default configuration. */\nconst DEFAULT_EXCLUDE = [\"authenticity_token\", \"_method\", \"utf8\"];\n/** localStorage key prefix so drafts never clobber unrelated app storage. */\nconst STORAGE_PREFIX = \"stimeo--persist:\";\n/** Separator for disambiguating repeated same-name fields. */\nconst OCCURRENCE_SEP = \"\\u0000\";\n/** The one observed attribute that can move fields between nested Persist hosts. */\nconst OWNERSHIP_ATTRIBUTE = \"data-controller\";\nconst DEFAULT_DEBOUNCE = 400;\n\n/** Whether a parsed JSON value is a plain object-like record. */\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null && !Array.isArray(value);\n\n/** Whether a parsed JSON value belongs to the persisted field-value union. */\nconst isPersistValue = (value: unknown): value is PersistValue =>\n typeof value === \"string\" ||\n typeof value === \"boolean\" ||\n (Array.isArray(value) && value.every((item) => typeof item === \"string\"));\n\n/** Parses and validates the complete versioned payload without type assertions. */\nconst parsePayload = (raw: string): PersistPayload | null => {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return null;\n }\n if (!isRecord(parsed) || parsed.version !== 1 || !Array.isArray(parsed.fields)) return null;\n\n const fields: PersistPayloadField[] = [];\n const keys = new Set<string>();\n for (const candidate of parsed.fields) {\n if (!isRecord(candidate)) return null;\n const { key, value } = candidate;\n if (typeof key !== \"string\" || key.length === 0 || keys.has(key)) return null;\n if (!isPersistValue(value)) return null;\n keys.add(key);\n fields.push({ key, value });\n }\n return { version: 1, fields };\n};\n\n/** Whether a node is one of the native controls supported by Persist. */\nconst isPersistField = (node: unknown): node is PersistField =>\n node instanceof HTMLInputElement ||\n node instanceof HTMLTextAreaElement ||\n node instanceof HTMLSelectElement;\n\n/**\n * Headless draft-autosave behavior: persists form controls to `localStorage`\n * and restores them across Turbo navigation and reloads.\n *\n * Markup contract (identifier: `stimeo--persist`):\n * <form data-controller=\"stimeo--persist\"\n * data-stimeo--persist-key-value=\"contact-draft\">\n * <input name=\"name\">\n * <textarea name=\"message\"></textarea>\n * </form>\n *\n * The optional `field` targets restrict persistence to explicitly targeted\n * controls. The public `clear` action removes the active draft. `key`,\n * `debounce`, `exclude`, and `clearOn` Values can change while connected;\n * `clearOn` is an event name whose occurrence invokes `clear`.\n *\n * `restore`, `save`, and `clear` dispatch `{ key: string }`. `error` dispatches\n * `{ key: string, operation: \"read\" | \"write\" | \"remove\", reason:\n * \"unavailable\" | \"invalid-payload\" }`. Restore writes are intentionally\n * silent native-control mutations; consumers synchronize from the bubbling\n * `restore` event without treating a restored draft as user input.\n *\n * @remarks\n * Behavior only — restoration never moves focus. Passwords are never stored;\n * the default exclusion also omits Rails request metadata while preserving\n * application-owned hidden fields. Distinct logical keys isolate instances;\n * instances using the same key intentionally share one last-writer-wins draft.\n * Every listener, timer, observer, and queued reconciliation is released on\n * `disconnect()`, after any pending edit is flushed through the normal save\n * path. A reconnect removes stale restored state before reading storage.\n */\nexport class PersistController extends Controller<HTMLElement> {\n static override targets = [\"field\"];\n static override values = {\n key: { type: String, default: \"\" },\n debounce: { type: Number, default: DEFAULT_DEBOUNCE },\n // A malformed JSON list must fall back without aborting Stimulus connection.\n exclude: { type: String, default: \"\" },\n clearOn: { type: String, default: \"\" },\n };\n static actions = [\"clear\"] as const;\n static events = [\"restore\", \"save\", \"clear\", \"error\"] as const;\n\n declare readonly fieldTargets: Element[];\n declare readonly hasFieldTarget: boolean;\n\n declare keyValue: string;\n declare debounceValue: number;\n declare excludeValue: string;\n declare clearOnValue: string;\n\n readonly #timeouts = new SafeTimeout();\n readonly #restoreDynamic = new MicrotaskCoalescer(() => this.#restoreDynamicFields());\n readonly #observer = new MutationObserver((records) => this.#onMutations(records));\n #pendingSave: PendingSave | null = null;\n #connected = false;\n #logicalKey: string | null = null;\n #payload: PersistPayload | null = null;\n #registeredClearOn: string | null = null;\n #excluded: readonly string[] = DEFAULT_EXCLUDE;\n #knownFields = new WeakSet<PersistField>();\n #forcedRestore = new Set<PersistField>();\n\n readonly #onInput = (event: Event): void => {\n const field = event.target;\n if (!isPersistField(field) || !this.#ownsField(field)) return;\n if (this.hasFieldTarget && !this.fieldTargets.includes(field)) return;\n if (this.#keyOf(field) === null || !this.#persistable(field, this.#excluded)) return;\n this.#scheduleSave();\n };\n\n readonly #onClearEvent = (): void => {\n this.clear();\n };\n\n override connect(): void {\n if (this.#connected) return;\n this.#connected = true;\n this.#logicalKey = this.#resolveLogicalKey();\n this.#excluded = parseStringList(this.excludeValue, DEFAULT_EXCLUDE);\n this.#knownFields = new WeakSet<PersistField>();\n this.#forcedRestore.clear();\n\n this.element.addEventListener(\"input\", this.#onInput);\n this.element.addEventListener(\"change\", this.#onInput);\n this.#syncClearOn();\n this.#restoreDynamic.activate();\n this.#observer.observe(this.element, {\n attributes: true,\n attributeFilter: [\n \"checked\",\n \"data-controller\",\n `data-${this.identifier}-target`,\n \"id\",\n \"multiple\",\n \"name\",\n \"selected\",\n \"type\",\n \"value\",\n ],\n childList: true,\n subtree: true,\n });\n this.#loadActiveDraft();\n }\n\n override disconnect(): void {\n this.#flushPendingSave();\n this.#connected = false;\n this.element.removeEventListener(\"input\", this.#onInput);\n this.element.removeEventListener(\"change\", this.#onInput);\n this.#unbindClearOn();\n this.#observer.disconnect();\n this.#restoreDynamic.cancel();\n this.#forcedRestore.clear();\n this.#timeouts.clearAll();\n TRANSIENT.reset(this.element);\n this.#payload = null;\n }\n\n /** Drops the active draft after storage confirms the removal. */\n clear(): void {\n const logicalKey = this.#logicalKey ?? this.#resolveLogicalKey();\n if (logicalKey === null) return;\n this.#cancelPendingSave();\n const result = removeLocalStorage(this.#storageKey(logicalKey));\n if (!result.ok) {\n this.#dispatchError(logicalKey, \"remove\", \"unavailable\");\n return;\n }\n this.#payload = null;\n this.element.removeAttribute(\"data-persist-restored\");\n this.dispatch(\"clear\", { detail: { key: logicalKey } });\n }\n\n /** Rebinds the external clear event when its Value changes. */\n clearOnValueChanged(): void {\n if (this.#connected) this.#syncClearOn();\n }\n\n /** Switches storage namespaces without writing old edits under the new key. */\n keyValueChanged(): void {\n if (this.#connected) this.#switchLogicalKey();\n }\n\n /** Reschedules a pending write against the current debounce delay. */\n debounceValueChanged(): void {\n if (this.#connected && this.#pendingSave !== null) this.#scheduleSave();\n }\n\n /** Restores every field the new exclusion list makes eligible. */\n excludeValueChanged(): void {\n const previous = this.#excluded;\n this.#excluded = parseStringList(this.excludeValue, DEFAULT_EXCLUDE);\n if (!this.#connected) return;\n // A field restored once stays in #knownFields, so releasing it from the exclusion\n // list has to name it explicitly to reach the next batch. Fields the list never\n // covered keep their live value, which is what the next batch already guarantees.\n for (const field of this.#candidateFields()) {\n if (this.#persistable(field, this.#excluded) && !this.#persistable(field, previous)) {\n this.#forcedRestore.add(field);\n }\n }\n this.#restoreDynamic.schedule();\n }\n\n /** Schedules a debounced save. */\n #scheduleSave(): void {\n const logicalKey = this.#logicalKey;\n if (logicalKey === null) return;\n this.#cancelPendingSave();\n const id = this.#timeouts.set(() => {\n this.#pendingSave = null;\n this.#save(logicalKey);\n }, this.#debounceDelay);\n this.#pendingSave = { id, logicalKey };\n }\n\n /** Cancels the currently pending save, if any. */\n #cancelPendingSave(): void {\n const pending = this.#pendingSave;\n if (pending === null) return;\n this.#timeouts.clear(pending.id);\n this.#pendingSave = null;\n }\n\n /** Flushes a pending edit through the same success/error event path as a timer. */\n #flushPendingSave(): void {\n const pending = this.#pendingSave;\n if (pending === null) return;\n this.#cancelPendingSave();\n this.#save(pending.logicalKey);\n }\n\n /** Writes the active payload and emits `save` only after storage succeeds. */\n #save(logicalKey: string): boolean {\n const payload = this.#serialize();\n const result = writeLocalStorage(this.#storageKey(logicalKey), JSON.stringify(payload));\n if (!result.ok) {\n this.#dispatchError(logicalKey, \"write\", \"unavailable\");\n return false;\n }\n this.#payload = payload;\n this.dispatch(\"save\", { detail: { key: logicalKey } });\n return true;\n }\n\n /** Serializes every persistable field into a versioned, typed payload. */\n #serialize(): PersistPayload {\n const fields: PersistPayloadField[] = [];\n for (const { field, key } of this.#fieldEntries()) {\n if (field instanceof HTMLInputElement && field.type === \"checkbox\") {\n fields.push({ key, value: field.checked });\n } else if (field instanceof HTMLInputElement && field.type === \"radio\") {\n if (field.checked) fields.push({ key, value: field.value });\n } else if (field instanceof HTMLSelectElement && field.multiple) {\n fields.push({\n key,\n value: Array.from(field.selectedOptions).map((option) => option.value),\n });\n } else {\n fields.push({ key, value: field.value });\n }\n }\n return { version: 1, fields };\n }\n\n /** Reads, validates, and restores the active namespace. */\n #loadActiveDraft(): void {\n TRANSIENT.reset(this.element);\n this.#payload = null;\n this.#knownFields = new WeakSet<PersistField>();\n this.#forcedRestore.clear();\n const logicalKey = this.#logicalKey;\n if (logicalKey === null) {\n this.#markCurrentFieldsKnown();\n return;\n }\n\n const result = readLocalStorage(this.#storageKey(logicalKey));\n if (!result.ok) {\n this.#dispatchError(logicalKey, \"read\", \"unavailable\");\n this.#markCurrentFieldsKnown();\n return;\n }\n if (result.value === null) {\n this.#markCurrentFieldsKnown();\n return;\n }\n\n const payload = parsePayload(result.value);\n if (payload === null) {\n const removal = removeLocalStorage(this.#storageKey(logicalKey));\n // Cleanup precedes notification so an error consumer can safely replace\n // the invalid value without that repair being deleted on callback return.\n this.#dispatchError(logicalKey, \"read\", \"invalid-payload\");\n if (!removal.ok) this.#dispatchError(logicalKey, \"remove\", \"unavailable\");\n this.#markCurrentFieldsKnown();\n return;\n }\n this.#payload = payload;\n this.#restoreEntries(null);\n }\n\n /** Restores fields inserted or made eligible after the initial connection. */\n #restoreDynamicFields(): void {\n const selected = new Set<PersistField>(this.#forcedRestore);\n this.#forcedRestore.clear();\n for (const { field } of this.#fieldEntries()) {\n if (!this.#knownFields.has(field)) selected.add(field);\n }\n this.#restoreEntries(selected);\n }\n\n /** Applies the active payload to all fields, or only the supplied candidates. */\n #restoreEntries(selected: ReadonlySet<PersistField> | null): void {\n const entries = this.#fieldEntries();\n for (const { field } of entries) this.#knownFields.add(field);\n const payload = this.#payload;\n if (payload === null) return;\n const savedFields = new Map(payload.fields.map((field) => [field.key, field]));\n const restoredKeys = new Set<string>();\n const processedRadios = new Set<string>();\n\n for (const entry of entries) {\n if (selected !== null && !selected.has(entry.field)) continue;\n const saved = savedFields.get(entry.key);\n if (saved === undefined) continue;\n const { value } = saved;\n\n if (entry.field instanceof HTMLInputElement && entry.field.type === \"radio\") {\n if (processedRadios.has(entry.key)) continue;\n processedRadios.add(entry.key);\n const radios = entries\n .filter(\n ({ key, field }) =>\n key === entry.key && field instanceof HTMLInputElement && field.type === \"radio\",\n )\n .map(({ field }) => field as HTMLInputElement);\n if (typeof value !== \"string\" || !radios.some((radio) => radio.value === value)) continue;\n for (const radio of radios) radio.checked = radio.value === value;\n restoredKeys.add(entry.key);\n continue;\n }\n\n if (this.#applyValue(entry.field, value)) restoredKeys.add(entry.key);\n }\n\n if (restoredKeys.size > 0) {\n // Property writes can reflect to observed attributes in DOM implementations.\n // Drop only records produced by this restore before notifying consumers, so\n // authored mutations made from a restore listener remain observable.\n this.#observer.takeRecords();\n this.element.setAttribute(\"data-persist-restored\", \"true\");\n this.dispatch(\"restore\", { detail: { key: this.#logicalKey } });\n }\n }\n\n /** Applies a type-compatible stored value without coercion. */\n #applyValue(field: PersistField, value: PersistValue): boolean {\n if (field instanceof HTMLInputElement && field.type === \"checkbox\") {\n if (typeof value !== \"boolean\") return false;\n field.checked = value;\n return true;\n }\n if (field instanceof HTMLSelectElement && field.multiple) {\n if (!Array.isArray(value)) return false;\n const available = new Set(Array.from(field.options).map((option) => option.value));\n const selected = value.filter((candidate) => available.has(candidate));\n if (value.length > 0 && selected.length === 0) return false;\n const selectedValues = new Set(selected);\n for (const option of Array.from(field.options)) {\n option.selected = selectedValues.has(option.value);\n }\n return true;\n }\n if (typeof value !== \"string\") return false;\n if (field instanceof HTMLSelectElement) {\n const exists = Array.from(field.options).some((option) => option.value === value);\n if (!exists) return false;\n }\n field.value = value;\n return true;\n }\n\n /** Every control this instance owns, before the exclusion list narrows them. */\n #candidateFields(): PersistField[] {\n return this.hasFieldTarget\n ? this.fieldTargets.filter(isPersistField)\n : Array.from(this.element.querySelectorAll<PersistField>(\"input, textarea, select\")).filter(\n (field) => this.#ownsField(field),\n );\n }\n\n /**\n * Pairs persistable fields with DOM-order occurrence keys. Every HTML radio\n * group (same name and form owner) occupies one shared occurrence slot.\n */\n #fieldEntries(): Array<{ field: PersistField; key: string }> {\n const entries: Array<{ field: PersistField; key: string }> = [];\n const eligible: Array<{ field: PersistField; name: string }> = [];\n for (const field of this.#candidateFields()) {\n const name = this.#keyOf(field);\n if (name === null || !this.#persistable(field, this.#excluded)) continue;\n eligible.push({ field, name });\n }\n\n const nextOccurrence = new Map<string, number>();\n const radioSlots = new Map<string, Map<HTMLFormElement | null, string>>();\n const allocate = (name: string): string => {\n const occurrence = nextOccurrence.get(name) ?? 0;\n nextOccurrence.set(name, occurrence + 1);\n return occurrence === 0 ? name : `${name}${OCCURRENCE_SEP}${occurrence}`;\n };\n for (const { field, name } of eligible) {\n if (field instanceof HTMLInputElement && field.type === \"radio\") {\n const groups = radioSlots.get(name) ?? new Map<HTMLFormElement | null, string>();\n radioSlots.set(name, groups);\n let key = groups.get(field.form);\n if (key === undefined) {\n key = allocate(name);\n groups.set(field.form, key);\n }\n entries.push({ field, key });\n continue;\n }\n entries.push({ field, key: allocate(name) });\n }\n return entries;\n }\n\n /** Whether a field carries a restorable, non-excluded value. */\n #persistable(field: PersistField, excluded: readonly string[]): boolean {\n if (NON_VALUE_TYPES.has(field.type) || SENSITIVE_TYPES.has(field.type)) return false;\n if (excluded.includes(field.type)) return false;\n if (field.name.length > 0 && excluded.includes(field.name)) return false;\n return true;\n }\n\n /** A stable storage sub-key for a field (its name, else id). */\n #keyOf(field: PersistField): string | null {\n return field.name || field.id || null;\n }\n\n /** Whether this instance, rather than a nested Persist host, owns a field. */\n #ownsField(field: PersistField): boolean {\n return field.closest(`[data-controller~=\"${this.identifier}\"]`) === this.element;\n }\n\n /** Marks all currently eligible fields without applying a payload. */\n #markCurrentFieldsKnown(): void {\n for (const { field } of this.#fieldEntries()) this.#knownFields.add(field);\n }\n\n /** Switches namespace after flushing the old namespace's pending edit. */\n #switchLogicalKey(): void {\n const next = this.#resolveLogicalKey();\n if (next === this.#logicalKey) return;\n this.#flushPendingSave();\n this.#logicalKey = next;\n this.#loadActiveDraft();\n }\n\n /** Synchronizes the exact event name owned by the `clearOn` Value. */\n #syncClearOn(): void {\n const next = this.#validEventName(this.clearOnValue);\n if (next === this.#registeredClearOn) return;\n this.#unbindClearOn();\n if (next === null) return;\n this.element.addEventListener(next, this.#onClearEvent);\n this.#registeredClearOn = next;\n }\n\n /** Removes the event listener using the name that was actually registered. */\n #unbindClearOn(): void {\n if (this.#registeredClearOn === null) return;\n this.element.removeEventListener(this.#registeredClearOn, this.#onClearEvent);\n this.#registeredClearOn = null;\n }\n\n /** Returns a non-whitespace event type, or null when the hook is disabled. */\n #validEventName(value: string): string | null {\n const name = value.trim();\n return name.length > 0 && !/\\s/.test(name) ? name : null;\n }\n\n /**\n * Collects dynamic controls and select-option changes into one restore pass.\n *\n * @stimeoRuntimeOnly `key` decides whether an id change moves the storage namespace; it decides\n * nothing shown.\n */\n #onMutations(records: MutationRecord[]): void {\n let rootIdChanged = false;\n for (const record of records) {\n if (record.type === \"attributes\") {\n if (record.target === this.element && record.attributeName === \"id\") {\n rootIdChanged = true;\n }\n this.#collectAttributeCandidate(record.target, record.attributeName);\n continue;\n }\n for (const node of record.addedNodes) this.#collectRestoreCandidate(node);\n }\n if (rootIdChanged && this.keyValue.length === 0) this.#switchLogicalKey();\n this.#restoreDynamic.schedule();\n }\n\n /**\n * Adds the controls one mutated attribute can affect. Only an ownership change on a\n * descendant moves fields between Persist hosts, so that is the single case worth a\n * subtree sweep; every other observed attribute describes one control, and sweeping\n * from its container would re-apply the stored draft over edits still being debounced.\n */\n #collectAttributeCandidate(target: Node, attributeName: string | null): void {\n if (attributeName === OWNERSHIP_ATTRIBUTE && target !== this.element) {\n this.#collectRestoreCandidate(target);\n return;\n }\n this.#collectControlCandidate(target);\n }\n\n /** Adds a native control, or the select that owns a mutated option. */\n #collectControlCandidate(node: Node): void {\n if (isPersistField(node)) {\n this.#forcedRestore.add(node);\n return;\n }\n if (node instanceof HTMLOptionElement) {\n const select = node.closest(\"select\");\n if (select) this.#forcedRestore.add(select);\n }\n }\n\n /** Adds every control inside an inserted or newly owned subtree. */\n #collectRestoreCandidate(node: Node): void {\n this.#collectControlCandidate(node);\n if (!(node instanceof Element)) return;\n for (const field of node.querySelectorAll<PersistField>(\"input, textarea, select\")) {\n this.#forcedRestore.add(field);\n }\n }\n\n /**\n * Resolves the logical key from the Value, falling back to the host id.\n *\n * @stimeoRuntimeOnly `key` names the storage namespace; the restored fields come from what is\n * stored there.\n */\n #resolveLogicalKey(): string | null {\n const key = this.keyValue || this.element.id;\n return key.length > 0 ? key : null;\n }\n\n /** Namespaces one logical key within localStorage. */\n #storageKey(logicalKey: string): string {\n return `${STORAGE_PREFIX}${logicalKey}`;\n }\n\n /** Normalizes an invalid `debounce` Value to the `debounce` default. */\n get #debounceDelay(): number {\n const value = this.debounceValue;\n return Number.isFinite(value) && value >= 0 ? value : DEFAULT_DEBOUNCE;\n }\n\n /** Dispatches one observable storage failure without exposing browser errors. */\n #dispatchError(key: string, operation: PersistOperation, reason: PersistErrorReason): void {\n this.dispatch(\"error\", { detail: { key, operation, reason } });\n }\n}\n"]}
|