@webreflection/utils 0.2.13 → 0.2.14

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
@@ -11,6 +11,7 @@ A [collection](./src/) of utility functions:
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
15
  * **[shared-array-buffer](https://github.com/WebReflection/utils/tree/main/src#shared-array-buffer)** - to simulate *SAB* when not available
15
16
  * **[sticky](https://github.com/WebReflection/utils/tree/main/src#sticky)** - to stick once per realm anything useful
16
17
  * **[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.13",
3
+ "version": "0.2.14",
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
+ "./json-storage": {
32
+ "types": "./types/json-storage.d.ts",
33
+ "import": "./src/json-storage.js"
34
+ },
31
35
  "./shared-array-buffer": {
32
36
  "types": "./types/shared-array-buffer.d.ts",
33
37
  "import": "./src/shared-array-buffer.js"
@@ -47,6 +51,7 @@
47
51
  "ascii",
48
52
  "bound-once",
49
53
  "bound",
54
+ "json-storage",
50
55
  "shared-array-buffer",
51
56
  "sticky",
52
57
  "with-resolvers"
@@ -79,6 +84,11 @@
79
84
  "url": "https://github.com/WebReflection/utils/issues"
80
85
  },
81
86
  "homepage": "https://github.com/WebReflection/utils#readme",
87
+ "overrides": {
88
+ "c8": {
89
+ "yargs": "^18.0.0"
90
+ }
91
+ },
82
92
  "devDependencies": {
83
93
  "c8": "^11.0.0",
84
94
  "typescript": "^6.0.3"
package/src/README.md CHANGED
@@ -70,6 +70,26 @@ resolve(4);
70
70
 
71
71
  The **bound-once** variant ensures that repeated accesses, such as `boundOnce(Promise).all`, always return the same bound method.
72
72
 
73
+
74
+ ## json-storage
75
+
76
+ A *Map* like API that is compatible out of the box with *JSON API*.
77
+
78
+ ```js
79
+ import JSONStorage from '@webreflection/utils/json-storage';
80
+
81
+ const localJSONStorage = new JSONStorage;
82
+
83
+ // insert if not present an object and returns it
84
+ localJSONStorage.getOrInsert('key', { complex: true }).complex;
85
+
86
+ localJSONStorage.get('key').complex; // true
87
+ ```
88
+
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.
90
+
91
+
92
+
73
93
  ## shared-array-buffer
74
94
 
75
95
  This utility provides an unobtrusive *SAB* (*SharedArrayBuffer*) shim based on the default *ArrayBuffer*, with `grow(length)` and `growable` additions.
@@ -0,0 +1,167 @@
1
+ // @ts-check
2
+
3
+ const { entries, keys, values } = Object;
4
+
5
+ /** @type {typeof Symbol.iterator} */
6
+ const iterator = Symbol.iterator;
7
+
8
+ const LOCAL = 'local';
9
+ const SESSION = 'session';
10
+
11
+ /**
12
+ * @typedef {typeof LOCAL | typeof SESSION} JSONStorageKind
13
+ */
14
+
15
+ /**
16
+ * @template Value
17
+ * @typedef {{
18
+ * parse(source: string): Value,
19
+ * stringify(value: Value): string
20
+ * }} JSONStorageAPI
21
+ */
22
+
23
+ /**
24
+ * @typedef {{
25
+ * clear(): void,
26
+ * getItem(key: string): string | null,
27
+ * removeItem(key: string): void,
28
+ * setItem(key: string, value: string): void,
29
+ * [Symbol.iterator](): Generator<[string, string], void, unknown>,
30
+ * }} StorageAPI
31
+ */
32
+
33
+ /**
34
+ * @template Value
35
+ * @param {JSONStorage<Value>} self
36
+ * @param {string} key
37
+ * @param {Value} value
38
+ * @returns {Value}
39
+ */
40
+ const add = (self, key, value) => {
41
+ self.set(key, value);
42
+ return value;
43
+ };
44
+
45
+ /**
46
+ * Map-like facade over `localStorage` or `sessionStorage` that stores values
47
+ * through a JSON-compatible `parse`/`stringify` pair.
48
+ *
49
+ * @template [Value=unknown]
50
+ * @implements {Iterable<[string, Value]>}
51
+ */
52
+ export default class JSONStorage {
53
+ /** @type {typeof LOCAL} */
54
+ static LOCAL = LOCAL;
55
+
56
+ /** @type {typeof SESSION} */
57
+ static SESSION = SESSION;
58
+
59
+ /** @type {StorageAPI} */
60
+ #storage;
61
+
62
+ /** @type {(value: Value) => string} */
63
+ #stringify;
64
+
65
+ /** @type {(source: string) => Value} */
66
+ #parse;
67
+
68
+ /**
69
+ * @param {JSONStorageKind} [storage=LOCAL]
70
+ * @param {JSONStorageAPI<Value>} [json=JSON]
71
+ */
72
+ constructor(storage = LOCAL, { parse, stringify } = /** @type {JSONStorageAPI<Value>} */ (JSON)) {
73
+ // @ts-ignore
74
+ this.#storage = storage === LOCAL ? localStorage : sessionStorage;
75
+ this.#stringify = stringify;
76
+ this.#parse = parse;
77
+ }
78
+
79
+ /** @returns {void} */
80
+ clear() {
81
+ this.#storage.clear();
82
+ }
83
+
84
+ /**
85
+ * @param {string} key
86
+ * @returns {boolean}
87
+ */
88
+ delete(key) {
89
+ const had = this.has(key);
90
+ if (had) this.#storage.removeItem(key);
91
+ return had;
92
+ }
93
+
94
+ /**
95
+ * @param {string} key
96
+ * @returns {Value | undefined}
97
+ */
98
+ get(key) {
99
+ const value = this.#storage.getItem(key);
100
+ return typeof value === 'string' ? this.#parse(value) : void 0;
101
+ }
102
+
103
+ /**
104
+ * @param {string} key
105
+ * @param {Value} value
106
+ * @returns {Value}
107
+ */
108
+ getOrInsert(key, value) {
109
+ const current = this.get(key);
110
+ return current !== void 0 ? current : add(this, key, value);
111
+ }
112
+
113
+ /**
114
+ * @template {string} Key
115
+ * @param {Key} key
116
+ * @param {(key: Key) => Value} callback
117
+ * @returns {Value}
118
+ */
119
+ getOrInsertComputed(key, callback) {
120
+ const current = this.get(key);
121
+ return current !== void 0 ? current : add(this, key, callback(key));
122
+ }
123
+
124
+ /**
125
+ * @param {string} key
126
+ * @returns {boolean}
127
+ */
128
+ has(key) {
129
+ return this.#storage.getItem(key) != null;
130
+ }
131
+
132
+ /**
133
+ * @param {string} key
134
+ * @param {Value} value
135
+ * @returns {this}
136
+ */
137
+ set(key, value) {
138
+ this.#storage.setItem(key, this.#stringify(value));
139
+ return this;
140
+ }
141
+
142
+ /** @returns {Generator<[string, Value], void, unknown>} */
143
+ *entries() {
144
+ yield* this[iterator]();
145
+ }
146
+
147
+ /** @returns {Generator<string, void, unknown>} */
148
+ *keys() {
149
+ yield* keys(this.#storage);
150
+ }
151
+
152
+ /** @returns {Generator<Value, void, unknown>} */
153
+ *values() {
154
+ for (const value of values(this.#storage)) {
155
+ // @ts-ignore
156
+ yield this.#parse(value);
157
+ }
158
+ }
159
+
160
+ /** @returns {Generator<[string, Value], void, unknown>} */
161
+ *[iterator]() {
162
+ for (const [key, value] of entries(this.#storage)) {
163
+ // @ts-ignore
164
+ yield [key, this.#parse(value)];
165
+ }
166
+ }
167
+ }
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Map-like facade over `localStorage` or `sessionStorage` that stores values
3
+ * through a JSON-compatible `parse`/`stringify` pair.
4
+ *
5
+ * @template [Value=unknown]
6
+ * @implements {Iterable<[string, Value]>}
7
+ */
8
+ export default class JSONStorage<Value = unknown> implements Iterable<[string, Value]> {
9
+ /** @type {typeof LOCAL} */
10
+ static LOCAL: typeof LOCAL;
11
+ /** @type {typeof SESSION} */
12
+ static SESSION: typeof SESSION;
13
+ /**
14
+ * @param {JSONStorageKind} [storage=LOCAL]
15
+ * @param {JSONStorageAPI<Value>} [json=JSON]
16
+ */
17
+ constructor(storage?: JSONStorageKind, { parse, stringify }?: JSONStorageAPI<Value>);
18
+ /** @returns {void} */
19
+ clear(): void;
20
+ /**
21
+ * @param {string} key
22
+ * @returns {boolean}
23
+ */
24
+ delete(key: string): boolean;
25
+ /**
26
+ * @param {string} key
27
+ * @returns {Value | undefined}
28
+ */
29
+ get(key: string): Value | undefined;
30
+ /**
31
+ * @param {string} key
32
+ * @param {Value} value
33
+ * @returns {Value}
34
+ */
35
+ getOrInsert(key: string, value: Value): Value;
36
+ /**
37
+ * @template {string} Key
38
+ * @param {Key} key
39
+ * @param {(key: Key) => Value} callback
40
+ * @returns {Value}
41
+ */
42
+ getOrInsertComputed<Key extends string>(key: Key, callback: (key: Key) => Value): Value;
43
+ /**
44
+ * @param {string} key
45
+ * @returns {boolean}
46
+ */
47
+ has(key: string): boolean;
48
+ /**
49
+ * @param {string} key
50
+ * @param {Value} value
51
+ * @returns {this}
52
+ */
53
+ set(key: string, value: Value): this;
54
+ /** @returns {Generator<[string, Value], void, unknown>} */
55
+ entries(): Generator<[string, Value], void, unknown>;
56
+ /** @returns {Generator<string, void, unknown>} */
57
+ keys(): Generator<string, void, unknown>;
58
+ /** @returns {Generator<Value, void, unknown>} */
59
+ values(): Generator<Value, void, unknown>;
60
+ /** @returns {Generator<[string, Value], void, unknown>} */
61
+ [Symbol.iterator](): Generator<[string, Value], void, unknown>;
62
+ #private;
63
+ }
64
+ export type JSONStorageKind = typeof LOCAL | typeof SESSION;
65
+ export type JSONStorageAPI<Value> = {
66
+ parse(source: string): Value;
67
+ stringify(value: Value): string;
68
+ };
69
+ export type StorageAPI = {
70
+ clear(): void;
71
+ getItem(key: string): string | null;
72
+ removeItem(key: string): void;
73
+ setItem(key: string, value: string): void;
74
+ [Symbol.iterator](): Generator<[string, string], void, unknown>;
75
+ };
76
+ declare const LOCAL: "local";
77
+ declare const SESSION: "session";
78
+ export {};