Skip to content

Commit e8c999d

Browse files
committed
Update to Node.js 22, docs, and add type improvements
- Bump Node.js version to 22 in .nvmrc and docs - Improve README with clearer usage, dev, and API docs - Refine JSDoc types and generics in WebStorage class - Improve type safety and documentation throughout
1 parent 1b90dab commit e8c999d

File tree

4 files changed

+100
-52
lines changed

4 files changed

+100
-52
lines changed

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
20
1+
22

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,18 @@
3535
- Removed support for error callback functions in all methods (`getItem`, `setItem`, `removeItem`, `clear`, `keys`, `length`, `iterate`). Errors must now be handled via the returned tuple.
3636
- Internal `_keyPrefix` and `_driver` fields are now private class fields (`#keyPrefix`, `#driver`). They are no longer accessible outside the class.
3737

38+
## NEW FEATURES
39+
40+
- Export type declaration files (`.d.ts`) for TypeScript users, ensuring better type safety and autocompletion support in TypeScript projects.
41+
3842
### INTERNAL CHANGES
3943

4044
- Rewrite to use native class private fields.
4145
- Internal noopStorage fallback now fully conforms to the Storage interface.
4246
- Drop Jest in favor of @web/test-runner and Playwright for testing.
4347
- Drop rollup in favor of esbuild for bundling.
48+
- Update Node.js version requirement to 22.x.x.
49+
- Update dependencies to their latest versions.
4450

4551
## v2.1.0 (2021-01-26)
4652

README.md

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77

88
# WebStorage
99

10-
WebStorage is a lightweight JavaScript library that improves how you work with `localStorage` or `sessionStorage` by providing a clean, consistent API. It supports storing and retrieving any serializable value (not just strings) by automatically handling JSON encoding and decoding internally.
10+
`WebStorage` is a lightweight JavaScript library that improves how you work with `localStorage` or `sessionStorage` by providing a clean, consistent API. It supports storing and retrieving any serializable value (not just strings) by automatically handling JSON encoding and decoding internally.
1111

12-
A key feature of WebStorage is its use of namespacing via a configurable key prefix (default: `'web-storage/'`). This ensures that all stored items are scoped to your application, preventing collisions with other data in storage. For example, using a prefix like `'my-app/'` means calling clear() will only remove items with that prefix—leaving unrelated data untouched.
12+
A key feature of `WebStorage` is its use of namespacing via a configurable key prefix (default: `'web-storage/'`). This ensures that all stored items are scoped to your application, preventing collisions with other data in storage. For example, using a prefix like `'my-app/'` means calling clear() will only remove items with that prefix—leaving unrelated data untouched.
1313

14-
WebStorage is also designed with error handling in mind. Instead of throwing exceptions, all methods return a `[result, error]` tuple-style value allowing you to handle errors gracefully—or ignore them entirely—without needing `try...catch`.
14+
`WebStorage` is also designed with error handling in mind. Instead of throwing exceptions, all methods return a `[result, error]` tuple-style value allowing you to handle errors gracefully—or ignore them entirely—without needing `try...catch`.
1515

1616
## Install
1717

@@ -33,7 +33,7 @@ import { WebStorage } from '@georapbox/web-storage';
3333

3434
### new WebStorage(options = {})
3535

36-
Creates a new instance of the WebStorage with the specified options. The following options can be set:
36+
Creates a new instance of the `WebStorage` with the specified options. The following options can be set:
3737

3838
| Option | Type | Default | Description |
3939
| ------ | ---- | ------- | ----------- |
@@ -53,7 +53,7 @@ const myStore = new WebStorage({
5353

5454
### WebStorage.createInstance(options = {})
5555

56-
Same as the constructor, but returns a new instance of `WebStorage`. This is a convenience method to create an instance without using the `new` keyword.
56+
Creates and returns a new `WebStorage` instance, just like the constructor. This convenience method allows you to instantiate without using the `new` keyword.
5757

5858
**Example**
5959

@@ -108,7 +108,7 @@ const [saved, error] = myStore.setItem('somekey', { foo: 'bar' });
108108

109109
#### Note on value serialization
110110

111-
WebStorage uses `JSON.stringify()` internally to serialize values before saving them. While this supports most common JavaScript types, some special values are silently converted:
111+
`WebStorage` uses `JSON.stringify()` internally to serialize values before saving them. While this supports most common JavaScript types, some special values are silently converted:
112112

113113
- `NaN`, `Infinity`, `-Infinity`, and `undefined` → become null
114114
- Functions and symbols → are omitted or stored as `null/undefined`
@@ -202,6 +202,9 @@ const [len, error] = myStore.length();
202202

203203
Iterates over all saved items in storage for a specific datastore and execute a callback function for each key-value pair.
204204

205+
> [!IMPORTANT]
206+
> `iterate` does not guarantee the order of iteration. The order may vary depending on the browser implementation and storage driver used.
207+
205208
**Throws:** `TypeError` - Throws if `callback` is not a function.
206209
**Returns:** `[boolean, Error | null]` - Returns an array with two elements: the first is `true` if the iteration was successful, or `false` if it was not, and the second is `null` if no error occurred, or an `Error` object if an error occurred.
207210

@@ -219,34 +222,63 @@ const [iterated, error] = myStore.iterate((value, key) => {
219222

220223
## Development
221224

225+
Below are the instructions for setting up the development environment.
226+
227+
### Prerequisites
228+
229+
- Node.js (v22.x.x)
230+
- npm (v10.x.x)
231+
232+
### Installation
233+
234+
Clone the repository and install the dependencies:
235+
236+
```sh
237+
$ git clone [email protected]:georapbox/web-storage.git
238+
$ cd web-storage
239+
$ npm install
240+
```
241+
222242
### Build for development
223243

244+
Build the library for development and watch for any changes to the source files:
245+
224246
```sh
225247
$ npm run dev
226248
```
227249

228-
Builds the library for development and watches for any changes.
229-
230250
### Build for production
231251

252+
Build the library for production. This will create a minified version of the library in the `dist` directory.
253+
232254
```sh
233255
$ npm run build
234256
```
235257

236-
Builds the library for production; the output is minified and optimized for production use in the `dist` folder.
237-
238258
### Test
239259

260+
Run the tests to ensure everything is working correctly:
261+
240262
```sh
241263
$ npm test
242264
```
243265

244-
Runs the library tests. For coverage report, run:
266+
### Test with coverage
267+
268+
Generate a test coverage report. This will run the tests and generate a coverage report in the `coverage` directory.
245269

246270
```sh
247271
$ npm run test:coverage
248272
```
249273

274+
### Linting
275+
276+
Run the linter to check for any code style issues:
277+
278+
```sh
279+
$ npm run lint
280+
```
281+
250282
## Changelog
251283

252284
For API updates and breaking changes, check the [CHANGELOG][changelog].

src/web-storage.js

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
* A tuple representing either a successful value or an error.
1919
*
2020
* @template T
21-
* @typedef {[T, Error | null]} Result
21+
* @typedef {[T | null, Error | null]} Result<T>
2222
*/
2323

2424
const DEFAULT_DRIVER = 'localStorage';
@@ -97,11 +97,43 @@ class WebStorage {
9797
this.#keyPrefix = opts.keyPrefix.trim();
9898
}
9999

100+
/**
101+
* Checks if `storageType` is supported and is available.
102+
* Storage might be unavailable due to no browser support or due to being full or due to browser privacy settings.
103+
*
104+
* @param {WebStorageType} storageType - The storage type; available values "localStorage" or "sessionStorage".
105+
* @returns {boolean} - Returns `true` if `storage` available; otherwise `false`.
106+
*/
107+
static isAvailable(storageType) {
108+
try {
109+
const storage = window[storageType];
110+
const testKey = STORAGE_TEST_KEY;
111+
112+
storage.setItem(testKey, 'test');
113+
storage.getItem(testKey);
114+
storage.removeItem(testKey);
115+
return true;
116+
} catch {
117+
return false;
118+
}
119+
}
120+
121+
/**
122+
* Creates a new instance of WebStorage with the provided options.
123+
*
124+
* @param {WebStorageOptions} [options] - The options to configure the WebStorage instance.
125+
* @returns {WebStorage} - Returns a new instance of WebStorage.
126+
*/
127+
static createInstance(options) {
128+
return new WebStorage(options);
129+
}
130+
100131
/**
101132
* Saves an item to storage with the specified key.
102133
*
134+
* @template T
103135
* @param {string} key - The key under which to store the item.
104-
* @param {any} value - The item to save to the selected storage.
136+
* @param {T} value - The item to save to the selected storage.
105137
* @throws {TypeError} - Throws if `key` is not a string.
106138
* @returns {Result<boolean>} - Returns an array with two elements: the first is `true` if the item was saved successfully, or `false` if it was not, and the second is `null` if no error occurred, or an `Error` object if an error occurred.
107139
*/
@@ -123,9 +155,10 @@ class WebStorage {
123155
/**
124156
* Gets the saved item for the specified key from the storage for a specific datastore.
125157
*
158+
* @template T
126159
* @param {string} key - The key of the item to retrieve.
127160
* @throws {TypeError} - Throws if `key` is not a string.
128-
* @returns {Result<unknown>} - Returns an array with two elements: the first is the value of the saved item, and the second is `null` if no error occurred, or an `Error` object if an error occurred.
161+
* @returns {Result<T>} - Returns an array with two elements: the first is the value of the saved item, and the second is `null` if no error occurred, or an `Error` object if an error occurred.
129162
*/
130163
getItem(key) {
131164
if (typeof key !== 'string') {
@@ -134,7 +167,7 @@ class WebStorage {
134167

135168
try {
136169
const raw = this.#driver.getItem(this.#keyPrefix + key);
137-
return raw === null ? [null, null] : [JSON.parse(raw), null];
170+
return raw === null ? [null, null] : [/** @type {T} */ (JSON.parse(raw)), null];
138171
} catch (error) {
139172
return [null, error instanceof Error ? error : new Error(String(error))];
140173
}
@@ -188,6 +221,7 @@ class WebStorage {
188221
const unprefixedKey = removePrefix(key, this.#keyPrefix);
189222
result.push(unprefixedKey);
190223
});
224+
191225
return [result, null];
192226
} catch (error) {
193227
return [[], error instanceof Error ? error : new Error(String(error))];
@@ -201,13 +235,19 @@ class WebStorage {
201235
*/
202236
length() {
203237
const [keys, err] = this.keys();
204-
return err ? [0, err] : [keys.length, null];
238+
239+
if (!Array.isArray(keys) || err) {
240+
return [0, err];
241+
}
242+
243+
return [keys.length, null];
205244
}
206245

207246
/**
208247
* Iterates over all saved items in storage for a specific datastore and execute a callback function for each key-value pair.
209248
*
210-
* @param {(value: any, key: string) => void} iteratorCallback - The callback function to execute for each key-value pair.
249+
* @template T
250+
* @param {(value: T, key: string) => void} iteratorCallback - The callback function to execute for each key-value pair.
211251
* @throws {TypeError} - Throws if `iteratorCallback` is not a function.
212252
* @returns {Result<boolean>} - Returns an array with two elements: the first is `true` if the iteration was successful, or `false` if it was not, and the second is `null` if no error occurred, or an `Error` object if an error occurred.
213253
*/
@@ -219,9 +259,10 @@ class WebStorage {
219259
try {
220260
this.#iterateStorage((key, value) => {
221261
const unprefixedKey = removePrefix(key, this.#keyPrefix);
222-
const parsedValue = JSON.parse(value);
223-
iteratorCallback.call(this, parsedValue, unprefixedKey);
262+
const parsedValue = /** @type {T} */ (value === null ? null : JSON.parse(value));
263+
iteratorCallback(parsedValue, unprefixedKey);
224264
});
265+
225266
return [true, null];
226267
} catch (error) {
227268
return [false, error instanceof Error ? error : new Error(String(error))];
@@ -231,7 +272,7 @@ class WebStorage {
231272
/**
232273
* Iterates over all keys in the storage and executes a callback function for each key-value pair.
233274
*
234-
* @param {(key: string, value: any) => void} callback - The callback function to execute for each key-value pair.
275+
* @param {(key: string, value: string | null) => void} callback - The callback function to execute for each key-value pair.
235276
*/
236277
#iterateStorage(callback) {
237278
const keys = Object.keys(this.#driver);
@@ -244,37 +285,6 @@ class WebStorage {
244285
}
245286
}
246287
}
247-
248-
/**
249-
* Checks if `storageType` is supported and is available.
250-
* Storage might be unavailable due to no browser support or due to being full or due to browser privacy settings.
251-
*
252-
* @param {WebStorageType} storageType - The storage type; available values "localStorage" or "sessionStorage".
253-
* @returns {boolean} - Returns `true` if `storage` available; otherwise `false`.
254-
*/
255-
static isAvailable(storageType) {
256-
try {
257-
const storage = window[storageType];
258-
const testKey = STORAGE_TEST_KEY;
259-
260-
storage.setItem(testKey, 'test');
261-
storage.getItem(testKey);
262-
storage.removeItem(testKey);
263-
return true;
264-
} catch {
265-
return false;
266-
}
267-
}
268-
269-
/**
270-
* Creates a new instance of WebStorage with the provided options.
271-
*
272-
* @param {WebStorageOptions} [options] - The options to configure the WebStorage instance.
273-
* @returns {WebStorage} - Returns a new instance of WebStorage.
274-
*/
275-
static createInstance(options) {
276-
return new WebStorage(options);
277-
}
278288
}
279289

280290
export { WebStorage };

0 commit comments

Comments
 (0)