@webreflection/utils 0.2.14 → 0.2.16

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/README.md CHANGED
@@ -5,13 +5,14 @@
5
5
  <sup>**Social Media Photo by [benjamin lehman](https://unsplash.com/@abject) on [Unsplash](https://unsplash.com/)**</sup>
6
6
 
7
7
 
8
- A [collection](./src/) of utility functions:
8
+ A curated, *TypeScript* friendly, [collection](./src/) of utilities:
9
9
 
10
10
  * **[all](https://github.com/WebReflection/utils/tree/main/src#all)** - `Promise.all` via object destructuring
11
11
  * **[ascii](https://github.com/WebReflection/utils/tree/main/src#ascii)** - basic string to buffer conversion (without validation)
12
12
  * **[bound-once](https://github.com/WebReflection/utils/tree/main/src#bound-once)** - to retrieve unique bound methods per realm
13
13
  * **[bound](https://github.com/WebReflection/utils/tree/main/src#bound)** - to retrieve one-off bound methods
14
- * **[json-storage](https://github.com/WebReflection/utils/tree/main/src#json-storage)** - to handle with ease both `localStorage` and `sessionStorage` via a *Map* friendly API
14
+ * **[iterable](https://github.com/WebReflection/utils/tree/main/src#iterable)** - to make plain objects iterable as `Object.entries(object)` pairs, without touching objects that already are iterable
15
+ * **[json-storage](https://github.com/WebReflection/utils/tree/main/src#json-storage)** - to use `localStorage` or `sessionStorage` through a JSON-aware, iterable, *Map* friendly API
15
16
  * **[shared-array-buffer](https://github.com/WebReflection/utils/tree/main/src#shared-array-buffer)** - to simulate *SAB* when not available
16
17
  * **[sticky](https://github.com/WebReflection/utils/tree/main/src#sticky)** - to stick once per realm anything useful
17
18
  * **[with-resolvers](https://github.com/WebReflection/utils/tree/main/src#with-resolvers)** - a self bound `Promise.withResolver()` for older runtimes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webreflection/utils",
3
- "version": "0.2.14",
3
+ "version": "0.2.16",
4
4
  "type": "module",
5
5
  "types": {
6
6
  "./all": "./types/all.d.ts",
@@ -28,6 +28,10 @@
28
28
  "types": "./types/bound.d.ts",
29
29
  "import": "./src/bound.js"
30
30
  },
31
+ "./iterable": {
32
+ "types": "./types/iterable.d.ts",
33
+ "import": "./src/iterable.js"
34
+ },
31
35
  "./json-storage": {
32
36
  "types": "./types/json-storage.d.ts",
33
37
  "import": "./src/json-storage.js"
@@ -51,6 +55,7 @@
51
55
  "ascii",
52
56
  "bound-once",
53
57
  "bound",
58
+ "iterable",
54
59
  "json-storage",
55
60
  "shared-array-buffer",
56
61
  "sticky",
@@ -60,7 +65,7 @@
60
65
  "build": "npm run build:types && npm run test",
61
66
  "build:types": "tsc --allowJs --checkJs --declaration --emitDeclarationOnly --stripInternal --outDir types --target es2022 --lib es2024 --module nodenext --moduleResolution nodenext src/*.js",
62
67
  "coverage": "mkdir -p ./coverage; c8 report --reporter=text-lcov > ./coverage/lcov.info",
63
- "test": "c8 node -e 'let q=Promise.resolve();for(const t of require(`./package.json`).tests)q=q.then(()=>import(`./test/${t}.js`));'"
68
+ "test": "c8 node --localstorage-file './test/.storage' -e 'let q=Promise.resolve();for(const t of require(`./package.json`).tests)q=q.then(()=>import(`./test/${t}.js`));'"
64
69
  },
65
70
  "files": [
66
71
  "src",
package/src/README.md CHANGED
@@ -71,23 +71,64 @@ resolve(4);
71
71
  The **bound-once** variant ensures that repeated accesses, such as `boundOnce(Promise).all`, always return the same bound method.
72
72
 
73
73
 
74
+ ## iterable
75
+
76
+ Ensures an object can be consumed by `for...of`, spread, `Array.from`, and
77
+ other iterable-aware APIs.
78
+
79
+ ```js
80
+ import iterable from '@webreflection/utils/iterable';
81
+
82
+ const query = iterable({ page: 1, perPage: 20 });
83
+
84
+ console.log([...query]);
85
+ // [['page', 1], ['perPage', 20]]
86
+ ```
87
+
88
+ If the object already defines or inherits `Symbol.iterator`, it is returned
89
+ unchanged. Otherwise, the same object receives a configurable own
90
+ `Symbol.iterator` method that yields `Object.entries(ref)`.
91
+
92
+
74
93
  ## json-storage
75
94
 
76
- A *Map* like API that is compatible out of the box with *JSON API*.
95
+ A small *Map* like facade over `localStorage` by default, or `sessionStorage`
96
+ when requested. Values are serialized with `JSON.stringify` on write and parsed
97
+ with `JSON.parse` on read, so callers can store structured data without
98
+ manually converting every value.
77
99
 
78
100
  ```js
79
101
  import JSONStorage from '@webreflection/utils/json-storage';
80
102
 
81
- const localJSONStorage = new JSONStorage;
103
+ const preferences = new JSONStorage;
82
104
 
83
- // insert if not present an object and returns it
84
- localJSONStorage.getOrInsert('key', { complex: true }).complex;
105
+ preferences.set('theme', { dark: true });
85
106
 
86
- localJSONStorage.get('key').complex; // true
107
+ console.log(preferences.get('theme').dark);
108
+ // true
87
109
  ```
88
110
 
89
- The storage can be a session one via `new JSONStorage(JSONStorage.SESSION)` and it can optionally accept a different api from native *JSON* as long as both `parse` and `stringify` are implemented.
111
+ The API follows familiar `Map` names where they make sense: `get`, `set`,
112
+ `has`, `delete`, `clear`, `entries`, `keys`, `values`, and default iteration.
113
+ Missing keys return `undefined`, while `delete(key)` reports whether the key was
114
+ present.
115
+
116
+ ```js
117
+ const cart = new JSONStorage(JSONStorage.SESSION);
118
+
119
+ const items = cart.getOrInsert('items', []);
120
+ items.push('book');
121
+ cart.set('items', items);
122
+
123
+ for (const [key, value] of cart) {
124
+ console.log(key, value);
125
+ }
126
+ ```
90
127
 
128
+ Use `getOrInsert(key, value)` to create a value only when the key is absent, or
129
+ `getOrInsertComputed(key, callback)` when the initial value should be computed
130
+ from the key. A second constructor argument can replace the native *JSON* API as
131
+ long as it provides compatible `parse(source)` and `stringify(value)` methods.
91
132
 
92
133
 
93
134
  ## shared-array-buffer
@@ -0,0 +1,37 @@
1
+ // @ts-check
2
+
3
+ /** @type {typeof Symbol.iterator} */
4
+ const iterator = Symbol.iterator;
5
+
6
+ const { defineProperty: dp, entries } = Object;
7
+
8
+ /**
9
+ * @this {object}
10
+ * @returns {Generator<[string, any], void, unknown>}
11
+ */
12
+ function* value() {
13
+ yield* entries(this);
14
+ }
15
+
16
+ const configurable = true;
17
+
18
+ /**
19
+ * Ensures an object is iterable.
20
+ *
21
+ * If `ref` already defines or inherits `Symbol.iterator`, it is returned
22
+ * unchanged. Otherwise, the same object is augmented with `Symbol.iterator`
23
+ * yielding the entries produced by `Object.entries(ref)`.
24
+ *
25
+ * @typedef {{
26
+ * <T extends Iterable<unknown>>(ref: T): T;
27
+ * <T extends object>(ref: T): T & Iterable<[string, T[keyof T]]>;
28
+ * }} EnsureIterable
29
+ */
30
+
31
+ /**
32
+ * @param {object} ref
33
+ * @returns {object}
34
+ */
35
+ const iterable = ref => iterator in ref ? ref : dp(ref, iterator, { configurable, value });
36
+
37
+ export default /** @type {EnsureIterable} */(iterable);
@@ -0,0 +1,13 @@
1
+ declare const _default: EnsureIterable;
2
+ export default _default;
3
+ /**
4
+ * Ensures an object is iterable.
5
+ *
6
+ * If `ref` already defines or inherits `Symbol.iterator`, it is returned
7
+ * unchanged. Otherwise, the same object is augmented with `Symbol.iterator`
8
+ * yielding the entries produced by `Object.entries(ref)`.
9
+ */
10
+ export type EnsureIterable = {
11
+ <T extends Iterable<unknown>>(ref: T): T;
12
+ <T extends object>(ref: T): T & Iterable<[string, T[keyof T]]>;
13
+ };