@hyperfrontend/list-utils 0.0.5 → 0.1.0
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/CHANGELOG.md +10 -0
- package/README.md +4 -3
- package/SECURITY.md +50 -15
- package/bundle/index.iife.js +2 -195
- package/bundle/index.iife.min.js +1 -1
- package/bundle/index.umd.js +2 -195
- package/bundle/index.umd.min.js +1 -1
- package/index.cjs.js +5 -2
- package/index.d.ts +5 -2
- package/index.esm.js +5 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.1.0](https://github.com/AndrewRedican/hyperfrontend/compare/5f116abb8ba6355dfb283fa03b7481e5eb029480...cc16961a470190848f71a7cce41f7377bc4511a4) - 2026-08-25
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- **BREAKING** type fifo pull as T | undefined, reword throws
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
- **BREAKING** type fifo pull as T | undefined, reword throws
|
|
14
|
+
|
|
5
15
|
## [0.0.5](https://github.com/AndrewRedican/hyperfrontend/compare/c8db08be8b183addd26caf81fdd17fb3693f296f...466c0388c4cd516b9c704214140b4df1004098e6) - 2026-06-23
|
|
6
16
|
|
|
7
17
|
### Other
|
package/README.md
CHANGED
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
Purpose-built collection utilities for queue management, filtering, and iteration patterns.
|
|
37
37
|
|
|
38
38
|
• 👉 See [**documentation**](https://www.hyperfrontend.dev/docs/libraries/utils/list/)
|
|
39
|
+
• 👉 See [**guides & tutorials**](https://www.hyperfrontend.dev/docs/guides/?package=%40hyperfrontend%2Flist-utils)
|
|
39
40
|
|
|
40
41
|
## What is @hyperfrontend/list-utils?
|
|
41
42
|
|
|
@@ -60,11 +61,11 @@ Queue implementations return frozen objects to prevent external mutation while u
|
|
|
60
61
|
|
|
61
62
|
### Prevents Queue Bugs in Event-Driven Systems
|
|
62
63
|
|
|
63
|
-
Native arrays don't enforce uniqueness, making it easy to accidentally register the same event handler, task, or subscription multiple times.
|
|
64
|
+
Native arrays don't enforce uniqueness, making it easy to accidentally register the same event handler, task, or subscription multiple times. Both list kinds reject duplicates based on reference equality, eliminating a common source of memory leaks and duplicate processing in event loops, job queues, and observer patterns. A FIFO list throws on a duplicate push; a LIFO list ignores it.
|
|
64
65
|
|
|
65
66
|
### Simplifies Round-Robin and Cyclical Patterns
|
|
66
67
|
|
|
67
|
-
The value picker solves the boilerplate problem of cycling through options (load balancer endpoints, color schemes, retry strategies). No manual index tracking or modulo math
|
|
68
|
+
The value picker solves the boilerplate problem of cycling through options (load balancer endpoints, color schemes, retry strategies). No manual index tracking or modulo math, just call `next()`. Particularly useful for testing scenarios where you need predictable value rotation.
|
|
68
69
|
|
|
69
70
|
### Type-Safe String Sanitization
|
|
70
71
|
|
|
@@ -89,7 +90,7 @@ import { createFifoList, createValuePicker, nonEmptyStrings, uniqueStrings } fro
|
|
|
89
90
|
const taskQueue = createFifoList<{ id: string; execute: () => void }>()
|
|
90
91
|
taskQueue.push({ id: 'task1', execute: () => console.log('Task 1') })
|
|
91
92
|
taskQueue.push({ id: 'task2', execute: () => console.log('Task 2') })
|
|
92
|
-
const nextTask = taskQueue.pull() // Gets task1 (first in)
|
|
93
|
+
const nextTask = taskQueue.pull() // Gets task1 (first in), or undefined once the queue is drained
|
|
93
94
|
|
|
94
95
|
// Round-robin value picker
|
|
95
96
|
const colorPicker = createValuePicker(['red', 'blue', 'green'])
|
package/SECURITY.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Security Policy
|
|
2
2
|
|
|
3
|
+
## The Security Model
|
|
4
|
+
|
|
5
|
+
Before reporting or reviewing, read the model this project is built on:
|
|
6
|
+
**[Security Model](https://www.hyperfrontend.dev/docs/core-concepts/security)**. It is the canonical
|
|
7
|
+
statement of what hyperfrontend defends against and what it does not, and it is the reference every
|
|
8
|
+
other security claim in these docs points back to. In short:
|
|
9
|
+
|
|
10
|
+
- **The named adversary is co-resident script**: an analytics snippet, tag manager, compromised
|
|
11
|
+
dependency, or unknown page that embeds a feature URL. A host that deliberately installs a feature
|
|
12
|
+
is trusting it, the way it trusts any dependency; the controls exist to bound a trusted
|
|
13
|
+
feature's bad day, not to treat its authors as hostile.
|
|
14
|
+
- **Origin checks authenticate rooms, not speakers.** Once arbitrary script runs inside a page, no
|
|
15
|
+
message check distinguishes it from the application. Threats inside a page need Content Security
|
|
16
|
+
Policy, Trusted Types, dependency provenance, and server-side authorisation. That is a different
|
|
17
|
+
treatment, deliberately outside this model.
|
|
18
|
+
- **Three parties carry the security of an integration.** The browser enforces document isolation
|
|
19
|
+
and the frame's capability attributes; the protocol enforces the relationship (pinned
|
|
20
|
+
counterparts, gated handshake, validated payloads, versioned contracts, an optional encrypted
|
|
21
|
+
envelope); and **you** decide authorisation: `frame-ancestors`, backend checks, HTTPS, the
|
|
22
|
+
envelope you choose, and the containment posture you set.
|
|
23
|
+
|
|
24
|
+
A vulnerability report is most useful when it names which of those three the issue defeats.
|
|
25
|
+
|
|
3
26
|
## Reporting a Vulnerability
|
|
4
27
|
|
|
5
28
|
We take the security of hyperfrontend seriously. If you discover a security vulnerability, please help us protect our users by following responsible disclosure practices.
|
|
@@ -58,14 +81,30 @@ Thank you for helping keep hyperfrontend and its users safe!
|
|
|
58
81
|
|
|
59
82
|
## Security Best Practices
|
|
60
83
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
84
|
+
These are the decisions the SDK cannot make for you. Everything the protocol already enforces —
|
|
85
|
+
origin pinning, window binding, instance identity, the gated handshake, payload validation on both
|
|
86
|
+
ends — is on by default and is not something you should be re-implementing by hand.
|
|
87
|
+
|
|
88
|
+
1. **Restrict who may embed the feature.** Send
|
|
89
|
+
`Content-Security-Policy: frame-ancestors <hosts>` on the response that serves the feature
|
|
90
|
+
document. Origin pinning keeps a conversation consistent; only `frame-ancestors` decides whether
|
|
91
|
+
a page was ever allowed to frame you.
|
|
92
|
+
2. **Authorise on the server.** A message that crossed the boundary is not an authorised operation.
|
|
93
|
+
Protected work needs credentials the feature's own backend validates.
|
|
94
|
+
3. **Choose the envelope deliberately.** `v2` with a pre-shared key is the confidentiality control;
|
|
95
|
+
`v1` is time-window obfuscation and buys deterrence only. Provision and rotate the `v2` key
|
|
96
|
+
yourself; a key is never baked into a built artifact. A handshake that cannot agree on an
|
|
97
|
+
encrypted transport falls back to plaintext; where that would be unacceptable, drive
|
|
98
|
+
`@hyperfrontend/nexus` directly and set `security.mode: 'fail-closed'` on the channel so the
|
|
99
|
+
connection is denied instead.
|
|
100
|
+
4. **Declare schemas and a contract version.** Actions without a schema pass unvalidated, and a side
|
|
101
|
+
without a version always passes the compatibility gate. Both are how drift is caught early.
|
|
102
|
+
5. **Grant capability narrowly.** Delegate only the Permissions-Policy features the integration
|
|
103
|
+
needs, and price a `sandbox` posture against what the product actually requires.
|
|
104
|
+
6. **Serve everything over HTTPS**, host and feature alike.
|
|
105
|
+
7. **Keep dependencies updated** on both sides of the boundary, and pair that with the page-integrity
|
|
106
|
+
controls this model deliberately leaves to you: Content Security Policy, Trusted Types,
|
|
107
|
+
Subresource Integrity, and dependency provenance.
|
|
69
108
|
|
|
70
109
|
## Security Updates
|
|
71
110
|
|
|
@@ -73,10 +112,6 @@ Security updates will be released as patch versions and documented in the [CHANG
|
|
|
73
112
|
|
|
74
113
|
## Supported Versions
|
|
75
114
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
| ------- | ------------------ |
|
|
80
|
-
| 0.0.x | :white_check_mark: |
|
|
81
|
-
|
|
82
|
-
As the project matures, we will update this table to reflect our long-term support policy.
|
|
115
|
+
Security updates are provided for the latest published version of each `@hyperfrontend/*` package.
|
|
116
|
+
Older releases receive no backports. A long-term support policy will replace this section once the
|
|
117
|
+
packages settle on a stable release cadence.
|
package/bundle/index.iife.js
CHANGED
|
@@ -1,86 +1,19 @@
|
|
|
1
1
|
var HyperfrontendListUtils = (function (exports) {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Safe copies of Array built-in static methods.
|
|
6
|
-
*
|
|
7
|
-
* These references are captured at module initialization time to protect against
|
|
8
|
-
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
9
|
-
*
|
|
10
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/array
|
|
11
|
-
*/
|
|
12
4
|
const _Array = globalThis.Array;
|
|
13
|
-
/**
|
|
14
|
-
* (Safe copy) Determines whether the passed value is an Array.
|
|
15
|
-
*/
|
|
16
5
|
const isArray = _Array.isArray;
|
|
17
|
-
/**
|
|
18
|
-
* (Safe copy) Creates an array from an array-like or iterable object.
|
|
19
|
-
*/
|
|
20
6
|
const from = _Array.from;
|
|
21
7
|
|
|
22
|
-
/**
|
|
23
|
-
* Safe copies of Error built-ins via factory functions.
|
|
24
|
-
*
|
|
25
|
-
* Since constructors cannot be safely captured via Object.assign, this module
|
|
26
|
-
* provides factory functions that use Reflect.construct internally.
|
|
27
|
-
*
|
|
28
|
-
* These references are captured at module initialization time to protect against
|
|
29
|
-
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
30
|
-
*
|
|
31
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/error
|
|
32
|
-
*/
|
|
33
8
|
const _Error = globalThis.Error;
|
|
34
9
|
const _Reflect$1 = globalThis.Reflect;
|
|
35
|
-
/**
|
|
36
|
-
* (Safe copy) Creates a new Error using the captured Error constructor.
|
|
37
|
-
* Use this instead of `new Error()`.
|
|
38
|
-
*
|
|
39
|
-
* @param message - Optional error message.
|
|
40
|
-
* @param options - Optional error options.
|
|
41
|
-
* @returns A new Error instance.
|
|
42
|
-
*
|
|
43
|
-
* @example Creating Error instances
|
|
44
|
-
* ```typescript
|
|
45
|
-
* const error = createError('Operation failed')
|
|
46
|
-
* // With cause for error chaining
|
|
47
|
-
* const wrapped = createError('Request failed', { cause: originalError })
|
|
48
|
-
* ```
|
|
49
|
-
*/
|
|
50
10
|
const createError = (message, options) => _Reflect$1.construct(_Error, [message, options]);
|
|
51
11
|
|
|
52
|
-
/**
|
|
53
|
-
* Safe copies of Object built-in methods.
|
|
54
|
-
*
|
|
55
|
-
* These references are captured at module initialization time to protect against
|
|
56
|
-
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
57
|
-
*
|
|
58
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/object
|
|
59
|
-
*/
|
|
60
12
|
const _Object = globalThis.Object;
|
|
61
|
-
/**
|
|
62
|
-
* (Safe copy) Prevents modification of existing property attributes and values,
|
|
63
|
-
* and prevents the addition of new properties.
|
|
64
|
-
*/
|
|
65
13
|
const freeze = _Object.freeze;
|
|
66
14
|
|
|
67
15
|
const registeredClasses = [];
|
|
68
16
|
|
|
69
|
-
/**
|
|
70
|
-
* Returns the data type of the target.
|
|
71
|
-
* Uses native `typeof` operator, however, makes distinction between `null`, `array`, and `object`.
|
|
72
|
-
* Also, when classes are registered via `registerClass`, it checks if objects are instance of any known registered class.
|
|
73
|
-
*
|
|
74
|
-
* @param target - The target to get the data type of.
|
|
75
|
-
* @returns The data type of the target.
|
|
76
|
-
*
|
|
77
|
-
* @example Determining data types
|
|
78
|
-
* ```typescript
|
|
79
|
-
* getType([1, 2]) // 'array'
|
|
80
|
-
* getType({ a: 1 }) // 'object'
|
|
81
|
-
* getType(null) // 'null'
|
|
82
|
-
* ```
|
|
83
|
-
*/
|
|
84
17
|
const getType = (target) => {
|
|
85
18
|
if (target === null)
|
|
86
19
|
return 'null';
|
|
@@ -96,48 +29,18 @@ var HyperfrontendListUtils = (function (exports) {
|
|
|
96
29
|
return nativeDataType;
|
|
97
30
|
};
|
|
98
31
|
|
|
99
|
-
/**
|
|
100
|
-
* Safe Set factory for protected set construction.
|
|
101
|
-
*
|
|
102
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/set
|
|
103
|
-
*/
|
|
104
|
-
/* eslint-disable workspace/lib-require-jsdoc-example */
|
|
105
32
|
const _Set = globalThis.Set;
|
|
106
33
|
const _Reflect = globalThis.Reflect;
|
|
107
|
-
/**
|
|
108
|
-
* (Safe copy) Creates a new Set using the captured Set constructor.
|
|
109
|
-
* Use this instead of `new Set()`.
|
|
110
|
-
*
|
|
111
|
-
* @param iterable - Optional iterable of values.
|
|
112
|
-
* @returns A new Set instance.
|
|
113
|
-
*/
|
|
114
34
|
const createSet = (iterable) => _Reflect.construct(_Set, iterable ? [iterable] : []);
|
|
115
35
|
|
|
116
|
-
/**
|
|
117
|
-
* Creates a FIFO (First-In-First-Out) list.
|
|
118
|
-
*
|
|
119
|
-
* @template T The type of elements in the list, must be an object.
|
|
120
|
-
* @returns {FifoList<T>} A FIFO list with methods to manipulate and query the list.
|
|
121
|
-
* @example Creating and using FIFO queue
|
|
122
|
-
* ```typescript
|
|
123
|
-
* interface Task { id: number; name: string }
|
|
124
|
-
* const queue = createFifoList<Task>()
|
|
125
|
-
*
|
|
126
|
-
* queue.push({ id: 1, name: 'first' })
|
|
127
|
-
* queue.push({ id: 2, name: 'second' })
|
|
128
|
-
*
|
|
129
|
-
* queue.pull() // => { id: 1, name: 'first' }
|
|
130
|
-
* queue.pull() // => { id: 2, name: 'second' }
|
|
131
|
-
* ```
|
|
132
|
-
*/
|
|
133
36
|
function createFifoList() {
|
|
134
37
|
const list = createSet();
|
|
135
38
|
const push = (item) => {
|
|
136
39
|
if (getType(item) !== 'object') {
|
|
137
|
-
throw createError('A fifo list only non-primitive values');
|
|
40
|
+
throw createError('A fifo list only supports non-primitive values');
|
|
138
41
|
}
|
|
139
42
|
if (list.has(item)) {
|
|
140
|
-
throw createError('Cannot
|
|
43
|
+
throw createError('Cannot push an item that already exists in the list');
|
|
141
44
|
}
|
|
142
45
|
list.add(item);
|
|
143
46
|
};
|
|
@@ -169,23 +72,6 @@ var HyperfrontendListUtils = (function (exports) {
|
|
|
169
72
|
return freeze(result);
|
|
170
73
|
}
|
|
171
74
|
|
|
172
|
-
/**
|
|
173
|
-
* Creates a LIFO (Last-In-First-Out) list.
|
|
174
|
-
*
|
|
175
|
-
* @template T The type of elements in the list, must be an object.
|
|
176
|
-
* @returns {LifoList<T>} A LIFO list with methods to manipulate and query the list.
|
|
177
|
-
* @example Creating and using LIFO stack
|
|
178
|
-
* ```typescript
|
|
179
|
-
* interface HistoryEntry { url: string; timestamp: number }
|
|
180
|
-
* const history = createLifoList<HistoryEntry>()
|
|
181
|
-
*
|
|
182
|
-
* history.push({ url: '/home', timestamp: 1 })
|
|
183
|
-
* history.push({ url: '/about', timestamp: 2 })
|
|
184
|
-
*
|
|
185
|
-
* history.pull() // => { url: '/about', timestamp: 2 }
|
|
186
|
-
* history.pull() // => { url: '/home', timestamp: 1 }
|
|
187
|
-
* ```
|
|
188
|
-
*/
|
|
189
75
|
function createLifoList() {
|
|
190
76
|
const list = createSet();
|
|
191
77
|
const push = (item) => {
|
|
@@ -226,21 +112,6 @@ var HyperfrontendListUtils = (function (exports) {
|
|
|
226
112
|
return freeze(result);
|
|
227
113
|
}
|
|
228
114
|
|
|
229
|
-
/**
|
|
230
|
-
* Creates an array of numbers within a specified range (inclusive).
|
|
231
|
-
*
|
|
232
|
-
* @param start - The starting number of the range
|
|
233
|
-
* @param end - The ending number of the range (inclusive)
|
|
234
|
-
* @returns An array containing all numbers from start to end
|
|
235
|
-
* @example Generating number ranges
|
|
236
|
-
* ```typescript
|
|
237
|
-
* createRange(1, 5)
|
|
238
|
-
* // => [1, 2, 3, 4, 5]
|
|
239
|
-
*
|
|
240
|
-
* createRange(3, 3)
|
|
241
|
-
* // => [3]
|
|
242
|
-
* ```
|
|
243
|
-
*/
|
|
244
115
|
function createRange(start, end) {
|
|
245
116
|
const range = [];
|
|
246
117
|
for (let i = start; i <= end; i += 1) {
|
|
@@ -249,42 +120,16 @@ var HyperfrontendListUtils = (function (exports) {
|
|
|
249
120
|
return range;
|
|
250
121
|
}
|
|
251
122
|
|
|
252
|
-
/**
|
|
253
|
-
* Creates a value picker that cycles through a list of string values.
|
|
254
|
-
*
|
|
255
|
-
* @param values - The array of string values to cycle through
|
|
256
|
-
* @returns A ValuePicker instance with current and next methods
|
|
257
|
-
* @example Cycling through values
|
|
258
|
-
* ```typescript
|
|
259
|
-
* const colorPicker = createValuePicker(['red', 'green', 'blue'])
|
|
260
|
-
*
|
|
261
|
-
* colorPicker.current() // => 'red'
|
|
262
|
-
* colorPicker.next() // => 'red' (first call initializes)
|
|
263
|
-
* colorPicker.next() // => 'green'
|
|
264
|
-
* colorPicker.next() // => 'blue'
|
|
265
|
-
* colorPicker.next() // => 'red' (cycles back)
|
|
266
|
-
* ```
|
|
267
|
-
*/
|
|
268
123
|
function createValuePicker(values) {
|
|
269
124
|
if (!values || !values.length) {
|
|
270
125
|
throw createError('Expected values not to be an empty list.');
|
|
271
126
|
}
|
|
272
127
|
let index = -1;
|
|
273
|
-
/**
|
|
274
|
-
* Returns the current value without advancing the picker.
|
|
275
|
-
*
|
|
276
|
-
* @returns The current value in the list
|
|
277
|
-
*/
|
|
278
128
|
function current() {
|
|
279
129
|
if (index < 0)
|
|
280
130
|
index = 0;
|
|
281
131
|
return values[index];
|
|
282
132
|
}
|
|
283
|
-
/**
|
|
284
|
-
* Advances to and returns the next value, cycling back to the start when reaching the end.
|
|
285
|
-
*
|
|
286
|
-
* @returns The next value in the list
|
|
287
|
-
*/
|
|
288
133
|
function next() {
|
|
289
134
|
if (index < 0)
|
|
290
135
|
return current();
|
|
@@ -297,53 +142,15 @@ var HyperfrontendListUtils = (function (exports) {
|
|
|
297
142
|
};
|
|
298
143
|
}
|
|
299
144
|
|
|
300
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
301
|
-
/**
|
|
302
|
-
* Retrieves the final key from a Map by converting keys to an array.
|
|
303
|
-
*
|
|
304
|
-
* @param map - The Map to extract the last key from
|
|
305
|
-
* @returns The key that was inserted last, or undefined if map is empty
|
|
306
|
-
* @example Getting last key from Map
|
|
307
|
-
* ```typescript
|
|
308
|
-
* const userRoles = new Map<string, string>()
|
|
309
|
-
* userRoles.set('alice', 'admin')
|
|
310
|
-
* userRoles.set('bob', 'editor')
|
|
311
|
-
* userRoles.set('charlie', 'viewer')
|
|
312
|
-
*
|
|
313
|
-
* getLastKeyInMap(userRoles) // => 'charlie'
|
|
314
|
-
* ```
|
|
315
|
-
*/
|
|
316
145
|
const getLastKeyInMap = (map) => {
|
|
317
146
|
const items = [...map.keys()];
|
|
318
147
|
return items[items.length - 1];
|
|
319
148
|
};
|
|
320
149
|
|
|
321
|
-
/**
|
|
322
|
-
* Filters out empty, null, undefined, and whitespace-only strings from an array.
|
|
323
|
-
*
|
|
324
|
-
* @param values - The array of strings to filter
|
|
325
|
-
* @returns A new array containing only non-empty strings with content
|
|
326
|
-
* @example Filtering empty strings
|
|
327
|
-
* ```typescript
|
|
328
|
-
* nonEmptyStrings(['hello', '', ' ', 'world', null as unknown as string])
|
|
329
|
-
* // => ['hello', 'world']
|
|
330
|
-
* ```
|
|
331
|
-
*/
|
|
332
150
|
function nonEmptyStrings(values) {
|
|
333
151
|
return values.filter((value) => ![undefined, null, ''].includes(value) && value.trim() !== '');
|
|
334
152
|
}
|
|
335
153
|
|
|
336
|
-
/**
|
|
337
|
-
* Removes duplicate strings from an array, preserving insertion order.
|
|
338
|
-
*
|
|
339
|
-
* @param values - The array of strings to deduplicate
|
|
340
|
-
* @returns A new array containing only unique strings
|
|
341
|
-
* @example Deduplicating strings
|
|
342
|
-
* ```typescript
|
|
343
|
-
* uniqueStrings(['apple', 'banana', 'apple', 'cherry', 'banana'])
|
|
344
|
-
* // => ['apple', 'banana', 'cherry']
|
|
345
|
-
* ```
|
|
346
|
-
*/
|
|
347
154
|
function uniqueStrings(values) {
|
|
348
155
|
return from(createSet(values));
|
|
349
156
|
}
|
package/bundle/index.iife.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var HyperfrontendListUtils=function(e){"use strict";const t=globalThis.Array,r=t.isArray,n=t.from,o=globalThis.Error,i=globalThis.Reflect,
|
|
1
|
+
var HyperfrontendListUtils=function(e){"use strict";const t=globalThis.Array,r=t.isArray,n=t.from,o=globalThis.Error,i=globalThis.Reflect,s=(e,t)=>i.construct(o,[e,t]),l=globalThis.Object.freeze,a=[],u=e=>{if(null===e)return"null";const t=typeof e;if("object"===t){if(r(e))return"array";for(const t of a)if(e instanceof t)return t.name}return t},c=globalThis.Set,f=globalThis.Reflect,h=e=>f.construct(c,e?[e]:[]);return e.createFifoList=function(){const e=h();return l({push:t=>{if("object"!==u(t))throw s("A fifo list only supports non-primitive values");if(e.has(t))throw s("Cannot push an item that already exists in the list");e.add(t)},pull:()=>{const t=e.values().next().value;return e.delete(t),t},map:t=>n(e.values()).map(t),forEach:t=>{e.forEach(t)},remove:t=>e.delete(t),has:t=>e.has(t),size:()=>e.size,clear:()=>e.clear()})},e.createLifoList=function(){const e=h();return l({push:t=>{if("object"!==u(t))throw s("A lifo list only supports non-primitive values");e.add(t)},pull:()=>{const t=n(e).pop();return t&&e.delete(t),t},map:t=>n(e).map(t),forEach:t=>{e.forEach(t)},remove:t=>e.delete(t),has:t=>e.has(t),size:()=>e.size,clear:()=>{e.clear()}})},e.createRange=function(e,t){const r=[];for(let n=e;n<=t;n+=1)r.push(n);return r},e.createValuePicker=function(e){if(!e||!e.length)throw s("Expected values not to be an empty list.");let t=-1;function r(){return t<0&&(t=0),e[t]}return{current:r,next:function(){return t<0?r():(t=++t<e.length?t:0,e[t])}}},e.getLastKeyInMap=e=>{const t=[...e.keys()];return t[t.length-1]},e.nonEmptyStrings=function(e){return e.filter(e=>![void 0,null,""].includes(e)&&""!==e.trim())},e.uniqueStrings=function(e){return n(h(e))},e}({});
|
package/bundle/index.umd.js
CHANGED
|
@@ -4,86 +4,19 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.HyperfrontendListUtils = {}));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* Safe copies of Array built-in static methods.
|
|
9
|
-
*
|
|
10
|
-
* These references are captured at module initialization time to protect against
|
|
11
|
-
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
12
|
-
*
|
|
13
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/array
|
|
14
|
-
*/
|
|
15
7
|
const _Array = globalThis.Array;
|
|
16
|
-
/**
|
|
17
|
-
* (Safe copy) Determines whether the passed value is an Array.
|
|
18
|
-
*/
|
|
19
8
|
const isArray = _Array.isArray;
|
|
20
|
-
/**
|
|
21
|
-
* (Safe copy) Creates an array from an array-like or iterable object.
|
|
22
|
-
*/
|
|
23
9
|
const from = _Array.from;
|
|
24
10
|
|
|
25
|
-
/**
|
|
26
|
-
* Safe copies of Error built-ins via factory functions.
|
|
27
|
-
*
|
|
28
|
-
* Since constructors cannot be safely captured via Object.assign, this module
|
|
29
|
-
* provides factory functions that use Reflect.construct internally.
|
|
30
|
-
*
|
|
31
|
-
* These references are captured at module initialization time to protect against
|
|
32
|
-
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
33
|
-
*
|
|
34
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/error
|
|
35
|
-
*/
|
|
36
11
|
const _Error = globalThis.Error;
|
|
37
12
|
const _Reflect$1 = globalThis.Reflect;
|
|
38
|
-
/**
|
|
39
|
-
* (Safe copy) Creates a new Error using the captured Error constructor.
|
|
40
|
-
* Use this instead of `new Error()`.
|
|
41
|
-
*
|
|
42
|
-
* @param message - Optional error message.
|
|
43
|
-
* @param options - Optional error options.
|
|
44
|
-
* @returns A new Error instance.
|
|
45
|
-
*
|
|
46
|
-
* @example Creating Error instances
|
|
47
|
-
* ```typescript
|
|
48
|
-
* const error = createError('Operation failed')
|
|
49
|
-
* // With cause for error chaining
|
|
50
|
-
* const wrapped = createError('Request failed', { cause: originalError })
|
|
51
|
-
* ```
|
|
52
|
-
*/
|
|
53
13
|
const createError = (message, options) => _Reflect$1.construct(_Error, [message, options]);
|
|
54
14
|
|
|
55
|
-
/**
|
|
56
|
-
* Safe copies of Object built-in methods.
|
|
57
|
-
*
|
|
58
|
-
* These references are captured at module initialization time to protect against
|
|
59
|
-
* prototype pollution attacks. Import only what you need for tree-shaking.
|
|
60
|
-
*
|
|
61
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/object
|
|
62
|
-
*/
|
|
63
15
|
const _Object = globalThis.Object;
|
|
64
|
-
/**
|
|
65
|
-
* (Safe copy) Prevents modification of existing property attributes and values,
|
|
66
|
-
* and prevents the addition of new properties.
|
|
67
|
-
*/
|
|
68
16
|
const freeze = _Object.freeze;
|
|
69
17
|
|
|
70
18
|
const registeredClasses = [];
|
|
71
19
|
|
|
72
|
-
/**
|
|
73
|
-
* Returns the data type of the target.
|
|
74
|
-
* Uses native `typeof` operator, however, makes distinction between `null`, `array`, and `object`.
|
|
75
|
-
* Also, when classes are registered via `registerClass`, it checks if objects are instance of any known registered class.
|
|
76
|
-
*
|
|
77
|
-
* @param target - The target to get the data type of.
|
|
78
|
-
* @returns The data type of the target.
|
|
79
|
-
*
|
|
80
|
-
* @example Determining data types
|
|
81
|
-
* ```typescript
|
|
82
|
-
* getType([1, 2]) // 'array'
|
|
83
|
-
* getType({ a: 1 }) // 'object'
|
|
84
|
-
* getType(null) // 'null'
|
|
85
|
-
* ```
|
|
86
|
-
*/
|
|
87
20
|
const getType = (target) => {
|
|
88
21
|
if (target === null)
|
|
89
22
|
return 'null';
|
|
@@ -99,48 +32,18 @@
|
|
|
99
32
|
return nativeDataType;
|
|
100
33
|
};
|
|
101
34
|
|
|
102
|
-
/**
|
|
103
|
-
* Safe Set factory for protected set construction.
|
|
104
|
-
*
|
|
105
|
-
* @module @hyperfrontend/immutable-api-utils/built-in-copy/set
|
|
106
|
-
*/
|
|
107
|
-
/* eslint-disable workspace/lib-require-jsdoc-example */
|
|
108
35
|
const _Set = globalThis.Set;
|
|
109
36
|
const _Reflect = globalThis.Reflect;
|
|
110
|
-
/**
|
|
111
|
-
* (Safe copy) Creates a new Set using the captured Set constructor.
|
|
112
|
-
* Use this instead of `new Set()`.
|
|
113
|
-
*
|
|
114
|
-
* @param iterable - Optional iterable of values.
|
|
115
|
-
* @returns A new Set instance.
|
|
116
|
-
*/
|
|
117
37
|
const createSet = (iterable) => _Reflect.construct(_Set, iterable ? [iterable] : []);
|
|
118
38
|
|
|
119
|
-
/**
|
|
120
|
-
* Creates a FIFO (First-In-First-Out) list.
|
|
121
|
-
*
|
|
122
|
-
* @template T The type of elements in the list, must be an object.
|
|
123
|
-
* @returns {FifoList<T>} A FIFO list with methods to manipulate and query the list.
|
|
124
|
-
* @example Creating and using FIFO queue
|
|
125
|
-
* ```typescript
|
|
126
|
-
* interface Task { id: number; name: string }
|
|
127
|
-
* const queue = createFifoList<Task>()
|
|
128
|
-
*
|
|
129
|
-
* queue.push({ id: 1, name: 'first' })
|
|
130
|
-
* queue.push({ id: 2, name: 'second' })
|
|
131
|
-
*
|
|
132
|
-
* queue.pull() // => { id: 1, name: 'first' }
|
|
133
|
-
* queue.pull() // => { id: 2, name: 'second' }
|
|
134
|
-
* ```
|
|
135
|
-
*/
|
|
136
39
|
function createFifoList() {
|
|
137
40
|
const list = createSet();
|
|
138
41
|
const push = (item) => {
|
|
139
42
|
if (getType(item) !== 'object') {
|
|
140
|
-
throw createError('A fifo list only non-primitive values');
|
|
43
|
+
throw createError('A fifo list only supports non-primitive values');
|
|
141
44
|
}
|
|
142
45
|
if (list.has(item)) {
|
|
143
|
-
throw createError('Cannot
|
|
46
|
+
throw createError('Cannot push an item that already exists in the list');
|
|
144
47
|
}
|
|
145
48
|
list.add(item);
|
|
146
49
|
};
|
|
@@ -172,23 +75,6 @@
|
|
|
172
75
|
return freeze(result);
|
|
173
76
|
}
|
|
174
77
|
|
|
175
|
-
/**
|
|
176
|
-
* Creates a LIFO (Last-In-First-Out) list.
|
|
177
|
-
*
|
|
178
|
-
* @template T The type of elements in the list, must be an object.
|
|
179
|
-
* @returns {LifoList<T>} A LIFO list with methods to manipulate and query the list.
|
|
180
|
-
* @example Creating and using LIFO stack
|
|
181
|
-
* ```typescript
|
|
182
|
-
* interface HistoryEntry { url: string; timestamp: number }
|
|
183
|
-
* const history = createLifoList<HistoryEntry>()
|
|
184
|
-
*
|
|
185
|
-
* history.push({ url: '/home', timestamp: 1 })
|
|
186
|
-
* history.push({ url: '/about', timestamp: 2 })
|
|
187
|
-
*
|
|
188
|
-
* history.pull() // => { url: '/about', timestamp: 2 }
|
|
189
|
-
* history.pull() // => { url: '/home', timestamp: 1 }
|
|
190
|
-
* ```
|
|
191
|
-
*/
|
|
192
78
|
function createLifoList() {
|
|
193
79
|
const list = createSet();
|
|
194
80
|
const push = (item) => {
|
|
@@ -229,21 +115,6 @@
|
|
|
229
115
|
return freeze(result);
|
|
230
116
|
}
|
|
231
117
|
|
|
232
|
-
/**
|
|
233
|
-
* Creates an array of numbers within a specified range (inclusive).
|
|
234
|
-
*
|
|
235
|
-
* @param start - The starting number of the range
|
|
236
|
-
* @param end - The ending number of the range (inclusive)
|
|
237
|
-
* @returns An array containing all numbers from start to end
|
|
238
|
-
* @example Generating number ranges
|
|
239
|
-
* ```typescript
|
|
240
|
-
* createRange(1, 5)
|
|
241
|
-
* // => [1, 2, 3, 4, 5]
|
|
242
|
-
*
|
|
243
|
-
* createRange(3, 3)
|
|
244
|
-
* // => [3]
|
|
245
|
-
* ```
|
|
246
|
-
*/
|
|
247
118
|
function createRange(start, end) {
|
|
248
119
|
const range = [];
|
|
249
120
|
for (let i = start; i <= end; i += 1) {
|
|
@@ -252,42 +123,16 @@
|
|
|
252
123
|
return range;
|
|
253
124
|
}
|
|
254
125
|
|
|
255
|
-
/**
|
|
256
|
-
* Creates a value picker that cycles through a list of string values.
|
|
257
|
-
*
|
|
258
|
-
* @param values - The array of string values to cycle through
|
|
259
|
-
* @returns A ValuePicker instance with current and next methods
|
|
260
|
-
* @example Cycling through values
|
|
261
|
-
* ```typescript
|
|
262
|
-
* const colorPicker = createValuePicker(['red', 'green', 'blue'])
|
|
263
|
-
*
|
|
264
|
-
* colorPicker.current() // => 'red'
|
|
265
|
-
* colorPicker.next() // => 'red' (first call initializes)
|
|
266
|
-
* colorPicker.next() // => 'green'
|
|
267
|
-
* colorPicker.next() // => 'blue'
|
|
268
|
-
* colorPicker.next() // => 'red' (cycles back)
|
|
269
|
-
* ```
|
|
270
|
-
*/
|
|
271
126
|
function createValuePicker(values) {
|
|
272
127
|
if (!values || !values.length) {
|
|
273
128
|
throw createError('Expected values not to be an empty list.');
|
|
274
129
|
}
|
|
275
130
|
let index = -1;
|
|
276
|
-
/**
|
|
277
|
-
* Returns the current value without advancing the picker.
|
|
278
|
-
*
|
|
279
|
-
* @returns The current value in the list
|
|
280
|
-
*/
|
|
281
131
|
function current() {
|
|
282
132
|
if (index < 0)
|
|
283
133
|
index = 0;
|
|
284
134
|
return values[index];
|
|
285
135
|
}
|
|
286
|
-
/**
|
|
287
|
-
* Advances to and returns the next value, cycling back to the start when reaching the end.
|
|
288
|
-
*
|
|
289
|
-
* @returns The next value in the list
|
|
290
|
-
*/
|
|
291
136
|
function next() {
|
|
292
137
|
if (index < 0)
|
|
293
138
|
return current();
|
|
@@ -300,53 +145,15 @@
|
|
|
300
145
|
};
|
|
301
146
|
}
|
|
302
147
|
|
|
303
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
304
|
-
/**
|
|
305
|
-
* Retrieves the final key from a Map by converting keys to an array.
|
|
306
|
-
*
|
|
307
|
-
* @param map - The Map to extract the last key from
|
|
308
|
-
* @returns The key that was inserted last, or undefined if map is empty
|
|
309
|
-
* @example Getting last key from Map
|
|
310
|
-
* ```typescript
|
|
311
|
-
* const userRoles = new Map<string, string>()
|
|
312
|
-
* userRoles.set('alice', 'admin')
|
|
313
|
-
* userRoles.set('bob', 'editor')
|
|
314
|
-
* userRoles.set('charlie', 'viewer')
|
|
315
|
-
*
|
|
316
|
-
* getLastKeyInMap(userRoles) // => 'charlie'
|
|
317
|
-
* ```
|
|
318
|
-
*/
|
|
319
148
|
const getLastKeyInMap = (map) => {
|
|
320
149
|
const items = [...map.keys()];
|
|
321
150
|
return items[items.length - 1];
|
|
322
151
|
};
|
|
323
152
|
|
|
324
|
-
/**
|
|
325
|
-
* Filters out empty, null, undefined, and whitespace-only strings from an array.
|
|
326
|
-
*
|
|
327
|
-
* @param values - The array of strings to filter
|
|
328
|
-
* @returns A new array containing only non-empty strings with content
|
|
329
|
-
* @example Filtering empty strings
|
|
330
|
-
* ```typescript
|
|
331
|
-
* nonEmptyStrings(['hello', '', ' ', 'world', null as unknown as string])
|
|
332
|
-
* // => ['hello', 'world']
|
|
333
|
-
* ```
|
|
334
|
-
*/
|
|
335
153
|
function nonEmptyStrings(values) {
|
|
336
154
|
return values.filter((value) => ![undefined, null, ''].includes(value) && value.trim() !== '');
|
|
337
155
|
}
|
|
338
156
|
|
|
339
|
-
/**
|
|
340
|
-
* Removes duplicate strings from an array, preserving insertion order.
|
|
341
|
-
*
|
|
342
|
-
* @param values - The array of strings to deduplicate
|
|
343
|
-
* @returns A new array containing only unique strings
|
|
344
|
-
* @example Deduplicating strings
|
|
345
|
-
* ```typescript
|
|
346
|
-
* uniqueStrings(['apple', 'banana', 'apple', 'cherry', 'banana'])
|
|
347
|
-
* // => ['apple', 'banana', 'cherry']
|
|
348
|
-
* ```
|
|
349
|
-
*/
|
|
350
157
|
function uniqueStrings(values) {
|
|
351
158
|
return from(createSet(values));
|
|
352
159
|
}
|
package/bundle/index.umd.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).HyperfrontendListUtils={})}(this,function(e){"use strict";const t=globalThis.Array,n=t.isArray,r=t.from,o=globalThis.Error,i=globalThis.Reflect,s=(e,t)=>i.construct(o,[e,t]),l=globalThis.Object.freeze,a=[],u=e=>{if(null===e)return"null";const t=typeof e;if("object"===t){if(n(e))return"array";for(const t of a)if(e instanceof t)return t.name}return t},c=globalThis.Set,f=globalThis.Reflect,h=e=>f.construct(c,e?[e]:[]);e.createFifoList=function(){const e=h();return l({push:t=>{if("object"!==u(t))throw s("A fifo list only non-primitive values");if(e.has(t))throw s("Cannot
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).HyperfrontendListUtils={})}(this,function(e){"use strict";const t=globalThis.Array,n=t.isArray,r=t.from,o=globalThis.Error,i=globalThis.Reflect,s=(e,t)=>i.construct(o,[e,t]),l=globalThis.Object.freeze,a=[],u=e=>{if(null===e)return"null";const t=typeof e;if("object"===t){if(n(e))return"array";for(const t of a)if(e instanceof t)return t.name}return t},c=globalThis.Set,f=globalThis.Reflect,h=e=>f.construct(c,e?[e]:[]);e.createFifoList=function(){const e=h();return l({push:t=>{if("object"!==u(t))throw s("A fifo list only supports non-primitive values");if(e.has(t))throw s("Cannot push an item that already exists in the list");e.add(t)},pull:()=>{const t=e.values().next().value;return e.delete(t),t},map:t=>r(e.values()).map(t),forEach:t=>{e.forEach(t)},remove:t=>e.delete(t),has:t=>e.has(t),size:()=>e.size,clear:()=>e.clear()})},e.createLifoList=function(){const e=h();return l({push:t=>{if("object"!==u(t))throw s("A lifo list only supports non-primitive values");e.add(t)},pull:()=>{const t=r(e).pop();return t&&e.delete(t),t},map:t=>r(e).map(t),forEach:t=>{e.forEach(t)},remove:t=>e.delete(t),has:t=>e.has(t),size:()=>e.size,clear:()=>{e.clear()}})},e.createRange=function(e,t){const n=[];for(let r=e;r<=t;r+=1)n.push(r);return n},e.createValuePicker=function(e){if(!e||!e.length)throw s("Expected values not to be an empty list.");let t=-1;function n(){return t<0&&(t=0),e[t]}return{current:n,next:function(){return t<0?n():(t=++t<e.length?t:0,e[t])}}},e.getLastKeyInMap=e=>{const t=[...e.keys()];return t[t.length-1]},e.nonEmptyStrings=function(e){return e.filter(e=>![void 0,null,""].includes(e)&&""!==e.trim())},e.uniqueStrings=function(e){return r(h(e))}});
|
package/index.cjs.js
CHANGED
|
@@ -21,16 +21,19 @@ const index_cjs_js = require('./_dependencies/@hyperfrontend/immutable-api-utils
|
|
|
21
21
|
*
|
|
22
22
|
* queue.pull() // => { id: 1, name: 'first' }
|
|
23
23
|
* queue.pull() // => { id: 2, name: 'second' }
|
|
24
|
+
* queue.pull() // => undefined (the list is empty)
|
|
24
25
|
* ```
|
|
26
|
+
* @throws {Error} When the pushed item is a primitive rather than an object.
|
|
27
|
+
* @throws {Error} When the pushed item is already in the list. Use has() to check first if a duplicate is expected.
|
|
25
28
|
*/
|
|
26
29
|
function createFifoList() {
|
|
27
30
|
const list = index_cjs_js.createSet();
|
|
28
31
|
const push = (item) => {
|
|
29
32
|
if (index_cjs_js$2.getType(item) !== 'object') {
|
|
30
|
-
throw index_cjs_js$3.createError('A fifo list only non-primitive values');
|
|
33
|
+
throw index_cjs_js$3.createError('A fifo list only supports non-primitive values');
|
|
31
34
|
}
|
|
32
35
|
if (list.has(item)) {
|
|
33
|
-
throw index_cjs_js$3.createError('Cannot
|
|
36
|
+
throw index_cjs_js$3.createError('Cannot push an item that already exists in the list');
|
|
34
37
|
}
|
|
35
38
|
list.add(item);
|
|
36
39
|
};
|
package/index.d.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
interface FifoList<T extends object> {
|
|
5
5
|
/** Adds an item to the end of the list */
|
|
6
6
|
push(item: T): void;
|
|
7
|
-
/** Removes and returns the first item in the list */
|
|
8
|
-
pull(): T;
|
|
7
|
+
/** Removes and returns the first item in the list, or undefined when the list is empty */
|
|
8
|
+
pull(): T | undefined;
|
|
9
9
|
/** Maps each item using the provided callback */
|
|
10
10
|
map<U>(callback: (item: T) => U): U[];
|
|
11
11
|
/** Iterates over each item in the list */
|
|
@@ -34,7 +34,10 @@ interface FifoList<T extends object> {
|
|
|
34
34
|
*
|
|
35
35
|
* queue.pull() // => { id: 1, name: 'first' }
|
|
36
36
|
* queue.pull() // => { id: 2, name: 'second' }
|
|
37
|
+
* queue.pull() // => undefined (the list is empty)
|
|
37
38
|
* ```
|
|
39
|
+
* @throws {Error} When the pushed item is a primitive rather than an object.
|
|
40
|
+
* @throws {Error} When the pushed item is already in the list. Use has() to check first if a duplicate is expected.
|
|
38
41
|
*/
|
|
39
42
|
declare function createFifoList<T extends object>(): FifoList<T>;
|
|
40
43
|
|
package/index.esm.js
CHANGED
|
@@ -19,16 +19,19 @@ import { createSet } from './_dependencies/@hyperfrontend/immutable-api-utils/bu
|
|
|
19
19
|
*
|
|
20
20
|
* queue.pull() // => { id: 1, name: 'first' }
|
|
21
21
|
* queue.pull() // => { id: 2, name: 'second' }
|
|
22
|
+
* queue.pull() // => undefined (the list is empty)
|
|
22
23
|
* ```
|
|
24
|
+
* @throws {Error} When the pushed item is a primitive rather than an object.
|
|
25
|
+
* @throws {Error} When the pushed item is already in the list. Use has() to check first if a duplicate is expected.
|
|
23
26
|
*/
|
|
24
27
|
function createFifoList() {
|
|
25
28
|
const list = createSet();
|
|
26
29
|
const push = (item) => {
|
|
27
30
|
if (getType(item) !== 'object') {
|
|
28
|
-
throw createError('A fifo list only non-primitive values');
|
|
31
|
+
throw createError('A fifo list only supports non-primitive values');
|
|
29
32
|
}
|
|
30
33
|
if (list.has(item)) {
|
|
31
|
-
throw createError('Cannot
|
|
34
|
+
throw createError('Cannot push an item that already exists in the list');
|
|
32
35
|
}
|
|
33
36
|
list.add(item);
|
|
34
37
|
};
|
package/package.json
CHANGED