@webreflection/utils 0.2.16 → 0.2.17
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 +12 -11
- package/package.json +6 -1
- package/src/README.md +46 -0
- package/src/registry.js +80 -0
- package/types/registry.d.ts +27 -0
package/README.md
CHANGED
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://coveralls.io/github/WebReflection/utils?branch=main)
|
|
4
4
|
|
|
5
|
-
<sup>**Social
|
|
5
|
+
<sup>**Social media photo by [Benjamin Lehman](https://unsplash.com/@abject) on [Unsplash](https://unsplash.com/)**</sup>
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
A curated, *TypeScript
|
|
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
|
-
* **[ascii](https://github.com/WebReflection/utils/tree/main/src#ascii)** - basic string
|
|
12
|
-
* **[bound-once](https://github.com/WebReflection/utils/tree/main/src#bound-once)** -
|
|
13
|
-
* **[bound](https://github.com/WebReflection/utils/tree/main/src#bound)** -
|
|
14
|
-
* **[iterable](https://github.com/WebReflection/utils/tree/main/src#iterable)** -
|
|
15
|
-
* **[json-storage](https://github.com/WebReflection/utils/tree/main/src#json-storage)** -
|
|
16
|
-
* **[
|
|
17
|
-
* **[
|
|
18
|
-
* **[
|
|
11
|
+
* **[ascii](https://github.com/WebReflection/utils/tree/main/src#ascii)** - basic string-to-buffer conversion without validation
|
|
12
|
+
* **[bound-once](https://github.com/WebReflection/utils/tree/main/src#bound-once)** - retrieve unique bound methods per realm
|
|
13
|
+
* **[bound](https://github.com/WebReflection/utils/tree/main/src#bound)** - retrieve one-off bound methods
|
|
14
|
+
* **[iterable](https://github.com/WebReflection/utils/tree/main/src#iterable)** - make plain objects iterable as `Object.entries(object)` pairs, without touching objects that are already iterable
|
|
15
|
+
* **[json-storage](https://github.com/WebReflection/utils/tree/main/src#json-storage)** - use `localStorage` or `sessionStorage` through a JSON-aware, iterable, *Map*-friendly API
|
|
16
|
+
* **[registry](https://github.com/WebReflection/utils/tree/main/src#registry)** - use a `Map`-like API with key/value validation and duplicate-key protection by default
|
|
17
|
+
* **[shared-array-buffer](https://github.com/WebReflection/utils/tree/main/src#shared-array-buffer)** - simulate *SAB* when not available
|
|
18
|
+
* **[sticky](https://github.com/WebReflection/utils/tree/main/src#sticky)** - keep useful values stable once per realm
|
|
19
|
+
* **[with-resolvers](https://github.com/WebReflection/utils/tree/main/src#with-resolvers)** - use a self-bound `Promise.withResolvers()` helper for older runtimes
|
|
19
20
|
|
|
20
|
-
MIT
|
|
21
|
+
MIT-style license.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webreflection/utils",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": {
|
|
6
6
|
"./all": "./types/all.d.ts",
|
|
@@ -36,6 +36,10 @@
|
|
|
36
36
|
"types": "./types/json-storage.d.ts",
|
|
37
37
|
"import": "./src/json-storage.js"
|
|
38
38
|
},
|
|
39
|
+
"./registry": {
|
|
40
|
+
"types": "./types/registry.d.ts",
|
|
41
|
+
"import": "./src/registry.js"
|
|
42
|
+
},
|
|
39
43
|
"./shared-array-buffer": {
|
|
40
44
|
"types": "./types/shared-array-buffer.d.ts",
|
|
41
45
|
"import": "./src/shared-array-buffer.js"
|
|
@@ -57,6 +61,7 @@
|
|
|
57
61
|
"bound",
|
|
58
62
|
"iterable",
|
|
59
63
|
"json-storage",
|
|
64
|
+
"registry",
|
|
60
65
|
"shared-array-buffer",
|
|
61
66
|
"sticky",
|
|
62
67
|
"with-resolvers"
|
package/src/README.md
CHANGED
|
@@ -131,6 +131,52 @@ from the key. A second constructor argument can replace the native *JSON* API as
|
|
|
131
131
|
long as it provides compatible `parse(source)` and `stringify(value)` methods.
|
|
132
132
|
|
|
133
133
|
|
|
134
|
+
## registry
|
|
135
|
+
|
|
136
|
+
A `Map` subclass that validates keys and values before storing them. By default,
|
|
137
|
+
keys must be unique, so setting the same key twice throws a `TypeError`; pass
|
|
138
|
+
`unique: false` when replacement should behave like a regular `Map`.
|
|
139
|
+
|
|
140
|
+
```js
|
|
141
|
+
import Registry from '@webreflection/utils/registry';
|
|
142
|
+
|
|
143
|
+
const registry = new Registry(null, {
|
|
144
|
+
key: value => typeof value === 'string',
|
|
145
|
+
value: value => typeof value === 'function'
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
registry.set('ready', () => true);
|
|
149
|
+
|
|
150
|
+
console.log(registry.get('ready')());
|
|
151
|
+
// true
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Both validators receive the candidate value and should return whether it is
|
|
155
|
+
allowed. In TypeScript-aware editors, type-predicate validators also define the
|
|
156
|
+
resulting `Registry<Key, Value>` shape, so `key` controls the map key type and
|
|
157
|
+
`value` controls the stored value type.
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
const mutable = new Registry(
|
|
161
|
+
[
|
|
162
|
+
['answer', 41],
|
|
163
|
+
['answer', 42]
|
|
164
|
+
],
|
|
165
|
+
{
|
|
166
|
+
key: value => value === 'answer',
|
|
167
|
+
value: value => Number.isInteger(value),
|
|
168
|
+
unique: false
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
console.log(mutable.get('answer'));
|
|
173
|
+
// 42
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Initial iterable entries are validated with the same rules used by `set()`, so
|
|
177
|
+
invalid keys, invalid values, or duplicate keys fail during construction.
|
|
178
|
+
|
|
179
|
+
|
|
134
180
|
## shared-array-buffer
|
|
135
181
|
|
|
136
182
|
This utility provides an unobtrusive *SAB* (*SharedArrayBuffer*) shim based on the default *ArrayBuffer*, with `grow(length)` and `growable` additions.
|
package/src/registry.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @template Type
|
|
5
|
+
* @typedef {((value: unknown) => value is Type) | ((value: unknown) => boolean)} RegistryValidator
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @template [Key=unknown]
|
|
10
|
+
* @template [Value=unknown]
|
|
11
|
+
* @typedef {{
|
|
12
|
+
* key?: RegistryValidator<Key>,
|
|
13
|
+
* value?: RegistryValidator<Value>,
|
|
14
|
+
* unique?: boolean,
|
|
15
|
+
* }} RegistryOptions
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {unknown[]} parts
|
|
20
|
+
*/
|
|
21
|
+
const fail = (...parts) => {
|
|
22
|
+
throw new TypeError(parts.map(String).join(' '));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** @type {RegistryOptions} */
|
|
26
|
+
const defaultOptions = {
|
|
27
|
+
key: _ => true,
|
|
28
|
+
value: _ => true,
|
|
29
|
+
unique: true,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Map with optional key/value validation and duplicate-key protection.
|
|
34
|
+
*
|
|
35
|
+
* @template [Key=unknown]
|
|
36
|
+
* @template [Value=unknown]
|
|
37
|
+
* @extends {Map<Key, Value>}
|
|
38
|
+
*/
|
|
39
|
+
export default class Registry extends Map {
|
|
40
|
+
/** @type {RegistryValidator<Key>} */
|
|
41
|
+
#key;
|
|
42
|
+
|
|
43
|
+
/** @type {RegistryValidator<Value>} */
|
|
44
|
+
#value;
|
|
45
|
+
|
|
46
|
+
/** @type {boolean} */
|
|
47
|
+
#unique;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {Iterable<[Key, Value]> | null} [iterable]
|
|
51
|
+
* @param {RegistryOptions<Key, Value>} [options]
|
|
52
|
+
*/
|
|
53
|
+
constructor(
|
|
54
|
+
iterable,
|
|
55
|
+
options = {}
|
|
56
|
+
) {
|
|
57
|
+
super();
|
|
58
|
+
const {
|
|
59
|
+
key = /** @type {RegistryValidator<Key>} */ (defaultOptions.key),
|
|
60
|
+
value = /** @type {RegistryValidator<Value>} */ (defaultOptions.value),
|
|
61
|
+
unique = defaultOptions.unique
|
|
62
|
+
} = options;
|
|
63
|
+
this.#key = key;
|
|
64
|
+
this.#value = value;
|
|
65
|
+
this.#unique = !!unique;
|
|
66
|
+
for (const [key, value] of iterable ?? []) this.set(key, value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {Key} key
|
|
71
|
+
* @param {Value} value
|
|
72
|
+
* @returns {this}
|
|
73
|
+
*/
|
|
74
|
+
set(key, value) {
|
|
75
|
+
if (!this.#key(key)) fail('Invalid key:', key);
|
|
76
|
+
if (this.#unique && super.has(key)) fail('Duplicate key:', key);
|
|
77
|
+
if (!this.#value(value)) fail('Invalid value:', value);
|
|
78
|
+
return super.set(key, value);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map with optional key/value validation and duplicate-key protection.
|
|
3
|
+
*
|
|
4
|
+
* @template [Key=unknown]
|
|
5
|
+
* @template [Value=unknown]
|
|
6
|
+
* @extends {Map<Key, Value>}
|
|
7
|
+
*/
|
|
8
|
+
export default class Registry<Key = unknown, Value = unknown> extends Map<Key, Value> {
|
|
9
|
+
/**
|
|
10
|
+
* @param {Iterable<[Key, Value]> | null} [iterable]
|
|
11
|
+
* @param {RegistryOptions<Key, Value>} [options]
|
|
12
|
+
*/
|
|
13
|
+
constructor(iterable?: Iterable<[Key, Value]> | null, options?: RegistryOptions<Key, Value>);
|
|
14
|
+
/**
|
|
15
|
+
* @param {Key} key
|
|
16
|
+
* @param {Value} value
|
|
17
|
+
* @returns {this}
|
|
18
|
+
*/
|
|
19
|
+
set(key: Key, value: Value): this;
|
|
20
|
+
#private;
|
|
21
|
+
}
|
|
22
|
+
export type RegistryValidator<Type> = ((value: unknown) => value is Type) | ((value: unknown) => boolean);
|
|
23
|
+
export type RegistryOptions<Key = unknown, Value = unknown> = {
|
|
24
|
+
key?: RegistryValidator<Key>;
|
|
25
|
+
value?: RegistryValidator<Value>;
|
|
26
|
+
unique?: boolean;
|
|
27
|
+
};
|