@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 CHANGED
@@ -2,19 +2,20 @@
2
2
 
3
3
  [![Coverage Status](https://coveralls.io/repos/github/WebReflection/utils/badge.svg?branch=main)](https://coveralls.io/github/WebReflection/utils?branch=main)
4
4
 
5
- <sup>**Social Media Photo by [benjamin lehman](https://unsplash.com/@abject) on [Unsplash](https://unsplash.com/)**</sup>
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* friendly, [collection](./src/) of utilities:
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 to buffer conversion (without validation)
12
- * **[bound-once](https://github.com/WebReflection/utils/tree/main/src#bound-once)** - to retrieve unique bound methods per realm
13
- * **[bound](https://github.com/WebReflection/utils/tree/main/src#bound)** - to retrieve one-off bound methods
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
16
- * **[shared-array-buffer](https://github.com/WebReflection/utils/tree/main/src#shared-array-buffer)** - to simulate *SAB* when not available
17
- * **[sticky](https://github.com/WebReflection/utils/tree/main/src#sticky)** - to stick once per realm anything useful
18
- * **[with-resolvers](https://github.com/WebReflection/utils/tree/main/src#with-resolvers)** - a self bound `Promise.withResolver()` for older runtimes
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 style License.
21
+ MIT-style license.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webreflection/utils",
3
- "version": "0.2.16",
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.
@@ -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
+ };