Skip to content

Commit 413371a

Browse files
Document secure storage behavior
Describe password exclusions, debounced autosaves, and typed storage error events across the README and package metadata. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 641daa1 commit 413371a

3 files changed

Lines changed: 84 additions & 5 deletions

File tree

‎README.md‎

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
A web component that stores (and restores) values within the first form it wraps.
66

77
It is designed for crash recovery and interrupted sessions:
8-
- Saves form field values to `localStorage` as users type.
8+
- Debounces saves to `localStorage` as users type.
99
- Restores values when the page is loaded again.
1010
- Clears saved values after a successful submit flow.
1111
- Optionally retains selected fields after submit.
12+
- Never saves or restores password fields.
1213

1314
## Demo
1415

@@ -150,14 +151,50 @@ Where `method` and `action` come from the wrapped form.
150151
| `restoreFormState()` | Applies saved values to current fields |
151152
| `clearSavedData()` | Removes saved values for the current form key |
152153

154+
Calls to `saveFormState()` save immediately. Saves triggered by `input` and
155+
`change` events use a short debounce to avoid serializing the complete form and
156+
writing to `localStorage` for every keystroke. Pending changes are flushed when
157+
the page is hidden.
158+
153159
## Field Support
154160

155161
Supported controls include:
156-
- `input` (except `type="file"`, `submit`, `button`, `reset`, and `image`)
162+
- `input` (except `type="password"`, `file`, `submit`, `button`, `reset`, and `image`)
157163
- `textarea`
158164
- `select` (single and multiple)
159165

160-
File fields are intentionally excluded.
166+
File and password fields are always excluded. Password values are neither saved
167+
nor restored, even if their field names appear in `retain`.
168+
169+
## Privacy and Storage Errors
170+
171+
Saved values are stored as plain text in `localStorage`. Any JavaScript running
172+
on the same origin can read them, so avoid wrapping forms that collect other
173+
sensitive information that should not persist on the device. The component
174+
automatically removes identifiable password entries written by earlier
175+
versions when it reads saved state.
176+
177+
If a browser blocks `localStorage`, its quota is exhausted, or another storage
178+
operation fails, the component dispatches a bubbling, composed
179+
`form-saver-error` event. Its `detail` contains:
180+
181+
```javascript
182+
{
183+
operation: 'read' | 'write' | 'remove',
184+
error: Error
185+
}
186+
```
187+
188+
For example:
189+
190+
```javascript
191+
document.querySelector('form-saver').addEventListener(
192+
'form-saver-error',
193+
({ detail }) => {
194+
console.error(`Could not ${detail.operation} saved form data`, detail.error);
195+
},
196+
);
197+
```
161198

162199
## Browser Support
163200

‎custom-elements.json‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,15 @@
7070
"privacy": "public"
7171
}
7272
],
73-
"events": [],
73+
"events": [
74+
{
75+
"name": "form-saver-error",
76+
"description": "Fired when localStorage cannot be read, written, or cleared.",
77+
"type": {
78+
"text": "CustomEvent<{ operation: 'read' | 'write' | 'remove'; error: unknown }>"
79+
}
80+
}
81+
],
7482
"attributes": [
7583
{
7684
"name": "retain",

‎form-saver.d.ts‎

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@
88
* @attr {string} retain-choice-label - Label text for the injected retention checkbox.
99
* @attr {string} retain-choice-container - Optional selector for a container element into which the retention checkbox is placed.
1010
* @attr {string} storage-key - Optional explicit storage key override.
11+
* @fires form-saver-error - Fired when localStorage cannot be read, written, or cleared.
1112
*/
13+
export interface FormSaverErrorDetail {
14+
operation: 'read' | 'write' | 'remove';
15+
error: unknown;
16+
}
17+
1218
export class FormSaverElement extends HTMLElement {
1319
/**
1420
* List of attributes to observe for changes.
@@ -57,7 +63,7 @@ export class FormSaverElement extends HTMLElement {
5763
set storageKey(value: string | null | undefined);
5864

5965
/**
60-
* Saves current form state into localStorage.
66+
* Immediately saves current form state into localStorage.
6167
*/
6268
saveFormState(): void;
6369

@@ -71,6 +77,34 @@ export class FormSaverElement extends HTMLElement {
7177
*/
7278
clearSavedData(): void;
7379

80+
addEventListener(
81+
type: 'form-saver-error',
82+
listener: (
83+
this: FormSaverElement,
84+
event: CustomEvent<FormSaverErrorDetail>,
85+
) => void,
86+
options?: boolean | AddEventListenerOptions,
87+
): void;
88+
addEventListener(
89+
type: string,
90+
listener: EventListenerOrEventListenerObject | null,
91+
options?: boolean | AddEventListenerOptions,
92+
): void;
93+
94+
removeEventListener(
95+
type: 'form-saver-error',
96+
listener: (
97+
this: FormSaverElement,
98+
event: CustomEvent<FormSaverErrorDetail>,
99+
) => void,
100+
options?: boolean | EventListenerOptions,
101+
): void;
102+
removeEventListener(
103+
type: string,
104+
listener: EventListenerOrEventListenerObject | null,
105+
options?: boolean | EventListenerOptions,
106+
): void;
107+
74108
/**
75109
* Upgrade a property to handle cases where it was set before the element upgraded.
76110
* @param prop - Property name to upgrade

0 commit comments

Comments
 (0)