@hyperfrontend/list-utils 0.0.5 → 0.1.1
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 +16 -0
- package/README.md +33 -34
- package/SECURITY.md +50 -15
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.cjs.js +0 -2
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/array/index.esm.js +1 -2
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.cjs.js +0 -2
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/object/index.esm.js +1 -2
- package/bundle/index.iife.js +6 -215
- package/bundle/index.iife.min.js +1 -1
- package/bundle/index.umd.js +6 -215
- package/bundle/index.umd.min.js +1 -1
- package/index.cjs.js +29 -13
- package/index.d.ts +5 -2
- package/index.esm.js +21 -5
- package/package.json +1 -1
- package/_dependencies/@hyperfrontend/data-utils/index.cjs.js +0 -214
- package/_dependencies/@hyperfrontend/data-utils/index.esm.js +0 -202
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/date/index.cjs.js +0 -8
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/date/index.esm.js +0 -7
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/map/index.cjs.js +0 -6
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/map/index.esm.js +0 -5
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.cjs.js +0 -7
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.esm.js +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.1.1](https://github.com/AndrewRedican/hyperfrontend/compare/29dc54d58c2b3c8b8dd35f7c546ebc6cd9191f6e...adf0a4f77dece2be855e7ab88185a4fe84e7b16b) - 2026-09-14
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- accept arrays, functions and class instances in fifo and lifo lists
|
|
10
|
+
|
|
11
|
+
## [0.1.0](https://github.com/AndrewRedican/hyperfrontend/compare/5f116abb8ba6355dfb283fa03b7481e5eb029480...cc16961a470190848f71a7cce41f7377bc4511a4) - 2026-08-25
|
|
12
|
+
|
|
13
|
+
### Breaking Changes
|
|
14
|
+
|
|
15
|
+
- **BREAKING** type fifo pull as T | undefined, reword throws
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
- **BREAKING** type fifo pull as T | undefined, reword throws
|
|
20
|
+
|
|
5
21
|
## [0.0.5](https://github.com/AndrewRedican/hyperfrontend/compare/c8db08be8b183addd26caf81fdd17fb3693f296f...466c0388c4cd516b9c704214140b4df1004098e6) - 2026-06-23
|
|
6
22
|
|
|
7
23
|
### Other
|
package/README.md
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://www.hyperfrontend.dev/docs/libraries/utils/list/">
|
|
3
|
+
<img width="640" height="180" src="https://www.hyperfrontend.dev/media/banner-list-utils/banner.gif" alt="@hyperfrontend/list-utils">
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
2
6
|
|
|
3
7
|
<p align="center">
|
|
4
8
|
<a href="https://github.com/AndrewRedican/hyperfrontend/actions/workflows/ci-lib-list-utils.yml">
|
|
@@ -33,9 +37,16 @@
|
|
|
33
37
|
<img src="https://img.shields.io/badge/tree%20shakeable-%E2%9C%93-success?style=flat-square" alt="Tree Shakeable">
|
|
34
38
|
</p>
|
|
35
39
|
|
|
40
|
+
<p align="center">
|
|
41
|
+
<a href="https://www.hyperfrontend.dev/docs/libraries/utils/list/">
|
|
42
|
+
<img width="640" height="360" src="https://www.hyperfrontend.dev/media/list-utils-order/hero.gif" alt="A tube open at both ends and a cup open only at the top, with the same three numbered discs dropped into each; three pulls take the discs out, the tube from its bottom and the cup from its top, and the exit rows read 1 2 3 under the tube and 3 2 1 beside the cup">
|
|
43
|
+
</a>
|
|
44
|
+
</p>
|
|
45
|
+
|
|
36
46
|
Purpose-built collection utilities for queue management, filtering, and iteration patterns.
|
|
37
47
|
|
|
38
48
|
• 👉 See [**documentation**](https://www.hyperfrontend.dev/docs/libraries/utils/list/)
|
|
49
|
+
• 👉 See [**guides & tutorials**](https://www.hyperfrontend.dev/docs/guides/?package=%40hyperfrontend%2Flist-utils)
|
|
39
50
|
|
|
40
51
|
## What is @hyperfrontend/list-utils?
|
|
41
52
|
|
|
@@ -45,26 +56,26 @@ The library enforces immutability through frozen interfaces while maintaining hi
|
|
|
45
56
|
|
|
46
57
|
### Key Features
|
|
47
58
|
|
|
48
|
-
- **FIFO and LIFO queues** with type-safe object tracking
|
|
49
|
-
- **Value picker** for cyclical iteration (ideal for round-robin patterns)
|
|
50
|
-
- **String sanitization** utilities (dedupe, trim, filter empty)
|
|
51
|
-
- **Range generation** for loop-free number sequences
|
|
52
|
-
- **Map utilities** for common Map operations
|
|
59
|
+
- **[FIFO and LIFO queues](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createFifoList)** with type-safe object tracking
|
|
60
|
+
- **[Value picker](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createValuePicker)** for cyclical iteration (ideal for round-robin patterns)
|
|
61
|
+
- **[String sanitization](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-uniqueStrings)** utilities (dedupe, trim, filter empty)
|
|
62
|
+
- **[Range generation](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createRange)** for loop-free number sequences
|
|
63
|
+
- **[Map utilities](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-getLastKeyInMap)** for common Map operations
|
|
53
64
|
- **Zero dependencies** - Self-contained implementation with no third-party runtime dependencies
|
|
54
65
|
|
|
55
66
|
### Architecture Highlights
|
|
56
67
|
|
|
57
|
-
|
|
68
|
+
Both queue kinds hold object references only: pushing a primitive throws at runtime, not just at compile time. Membership is reference identity, so two structurally identical objects are two separate entries, and `has()` and `remove()` need the same reference you pushed.
|
|
58
69
|
|
|
59
70
|
## Why Use @hyperfrontend/list-utils?
|
|
60
71
|
|
|
61
72
|
### Prevents Queue Bugs in Event-Driven Systems
|
|
62
73
|
|
|
63
|
-
Native arrays don't enforce uniqueness, making it easy to accidentally register the same event handler, task, or subscription multiple times.
|
|
74
|
+
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
75
|
|
|
65
76
|
### Simplifies Round-Robin and Cyclical Patterns
|
|
66
77
|
|
|
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
|
|
78
|
+
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
79
|
|
|
69
80
|
### Type-Safe String Sanitization
|
|
70
81
|
|
|
@@ -89,7 +100,7 @@ import { createFifoList, createValuePicker, nonEmptyStrings, uniqueStrings } fro
|
|
|
89
100
|
const taskQueue = createFifoList<{ id: string; execute: () => void }>()
|
|
90
101
|
taskQueue.push({ id: 'task1', execute: () => console.log('Task 1') })
|
|
91
102
|
taskQueue.push({ id: 'task2', execute: () => console.log('Task 2') })
|
|
92
|
-
const nextTask = taskQueue.pull() // Gets task1 (first in)
|
|
103
|
+
const nextTask = taskQueue.pull() // Gets task1 (first in), or undefined once the queue is drained
|
|
93
104
|
|
|
94
105
|
// Round-robin value picker
|
|
95
106
|
const colorPicker = createValuePicker(['red', 'blue', 'green'])
|
|
@@ -98,40 +109,28 @@ colorPicker.next() // 'blue'
|
|
|
98
109
|
colorPicker.next() // 'green'
|
|
99
110
|
colorPicker.next() // 'red' (cycles back)
|
|
100
111
|
|
|
101
|
-
// String sanitization
|
|
112
|
+
// String sanitization: blanks are dropped, survivors keep their original padding
|
|
102
113
|
const userInputs = [' hello ', '', 'world', null, 'hello', ' ', 'world']
|
|
103
|
-
const cleaned = uniqueStrings(nonEmptyStrings(userInputs)) // ['hello', 'world']
|
|
114
|
+
const cleaned = uniqueStrings(nonEmptyStrings(userInputs)) // [' hello ', 'world', 'hello']
|
|
104
115
|
```
|
|
105
116
|
|
|
106
117
|
## API Overview
|
|
107
118
|
|
|
108
|
-
|
|
119
|
+
Two ordered collections plus a few standalone helpers; the collections are the place to start. [`createFifoList`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createFifoList) and [`createLifoList`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createLifoList) each take no arguments, are generic over an object type, and return a frozen instance with the same fixed method surface: [`push`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList), [`pull`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList), [`map`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList), [`forEach`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList), [`remove`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList), [`has`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList), [`size`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList) and [`clear`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-FifoList). Choosing one factory over the other decides which end `pull()` reads from and nothing else about the surrounding code. They diverge on one point, deliberately: pushing an item a FIFO list already holds throws, surfacing a queue that would otherwise take the same job twice, whereas a LIFO list ignores the duplicate and leaves the item where it already was.
|
|
109
120
|
|
|
110
|
-
|
|
111
|
-
- **`createLifoList<T>()`** - Last-in-first-out stack with reference uniqueness
|
|
121
|
+
Both are backed by a `Set` of the references you pushed, which has two consequences to know before reaching for either. A primitive is rejected at runtime, not only by the type parameter. And membership is reference identity, so two structurally identical objects are two entries, and `has()` and `remove()` want back the exact reference you handed over.
|
|
112
122
|
|
|
113
|
-
|
|
123
|
+
The helpers are independent of the lists and of each other. [`createValuePicker`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createValuePicker) wraps a non-empty string array in a `current()` and `next()` pair that cycles forever, while [`createRange`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-createRange) returns an inclusive run of numbers. For string arrays, [`nonEmptyStrings`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-nonEmptyStrings) drops null, undefined, empty and whitespace-only entries but returns the survivors exactly as they arrived: it tests `value.trim()`, it does not trim, so padded values come through padded and [`uniqueStrings`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-uniqueStrings) then dedupes on exact string equality, which leaves `' hello '` and `'hello'` as two distinct results. The last helper, [`getLastKeyInMap`](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-getLastKeyInMap), reads the most recently inserted key of a `Map`.
|
|
114
124
|
|
|
115
|
-
|
|
116
|
-
- **`createRange(start, end)`** - Generate number arrays without loops
|
|
117
|
-
|
|
118
|
-
### String Utilities
|
|
119
|
-
|
|
120
|
-
- **`nonEmptyStrings(values)`** - Filter null/undefined/empty/whitespace strings
|
|
121
|
-
- **`uniqueStrings(values)`** - Remove duplicates while preserving order
|
|
122
|
-
|
|
123
|
-
### Map Helpers
|
|
124
|
-
|
|
125
|
-
- **`getLastKeyInMap(map)`** - Retrieve the last inserted key from a Map
|
|
125
|
+
Every signature, type parameter and thrown error is in the [API reference](https://www.hyperfrontend.dev/docs/libraries/utils/list/#api-reference).
|
|
126
126
|
|
|
127
127
|
## Compatibility
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
| Deno, Bun, Cloudflare Workers | ✅ |
|
|
129
|
+
<p align="center">
|
|
130
|
+
<a href="https://www.hyperfrontend.dev/docs/libraries/utils/list/#compatibility">
|
|
131
|
+
<img width="640" height="150" src="https://www.hyperfrontend.dev/media/runtimes-list-utils/runtimes.png" alt="Runs in Node.js 18 or later, evergreen browsers and web workers">
|
|
132
|
+
</a>
|
|
133
|
+
</p>
|
|
135
134
|
|
|
136
135
|
### Output Formats
|
|
137
136
|
|
|
@@ -156,7 +155,7 @@ const cleaned = uniqueStrings(nonEmptyStrings(userInputs)) // ['hello', 'world']
|
|
|
156
155
|
</script>
|
|
157
156
|
```
|
|
158
157
|
|
|
159
|
-
**Global variable:** `HyperfrontendListUtils`
|
|
158
|
+
**Global variable:** [`HyperfrontendListUtils`](https://www.hyperfrontend.dev/docs/libraries/utils/list/)
|
|
160
159
|
|
|
161
160
|
## Part of hyperfrontend
|
|
162
161
|
|
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,143 +1,29 @@
|
|
|
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
|
-
const isArray = _Array.isArray;
|
|
17
|
-
/**
|
|
18
|
-
* (Safe copy) Creates an array from an array-like or iterable object.
|
|
19
|
-
*/
|
|
20
5
|
const from = _Array.from;
|
|
21
6
|
|
|
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
7
|
const _Error = globalThis.Error;
|
|
34
8
|
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
9
|
const createError = (message, options) => _Reflect$1.construct(_Error, [message, options]);
|
|
51
10
|
|
|
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
11
|
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
12
|
const freeze = _Object.freeze;
|
|
66
13
|
|
|
67
|
-
const registeredClasses = [];
|
|
68
|
-
|
|
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
|
-
const getType = (target) => {
|
|
85
|
-
if (target === null)
|
|
86
|
-
return 'null';
|
|
87
|
-
const nativeDataType = typeof target;
|
|
88
|
-
if (nativeDataType === 'object') {
|
|
89
|
-
if (isArray(target))
|
|
90
|
-
return 'array';
|
|
91
|
-
for (const registeredClass of registeredClasses) {
|
|
92
|
-
if (target instanceof registeredClass)
|
|
93
|
-
return registeredClass.name;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return nativeDataType;
|
|
97
|
-
};
|
|
98
|
-
|
|
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
14
|
const _Set = globalThis.Set;
|
|
106
15
|
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
16
|
const createSet = (iterable) => _Reflect.construct(_Set, iterable ? [iterable] : []);
|
|
115
17
|
|
|
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
|
-
*/
|
|
18
|
+
const isNonPrimitive$1 = (value) => (typeof value === 'object' && value !== null) || typeof value === 'function';
|
|
133
19
|
function createFifoList() {
|
|
134
20
|
const list = createSet();
|
|
135
21
|
const push = (item) => {
|
|
136
|
-
if (
|
|
137
|
-
throw createError('A fifo list only non-primitive values');
|
|
22
|
+
if (!isNonPrimitive$1(item)) {
|
|
23
|
+
throw createError('A fifo list only supports non-primitive values');
|
|
138
24
|
}
|
|
139
25
|
if (list.has(item)) {
|
|
140
|
-
throw createError('Cannot
|
|
26
|
+
throw createError('Cannot push an item that already exists in the list');
|
|
141
27
|
}
|
|
142
28
|
list.add(item);
|
|
143
29
|
};
|
|
@@ -169,27 +55,11 @@ var HyperfrontendListUtils = (function (exports) {
|
|
|
169
55
|
return freeze(result);
|
|
170
56
|
}
|
|
171
57
|
|
|
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
|
-
*/
|
|
58
|
+
const isNonPrimitive = (value) => (typeof value === 'object' && value !== null) || typeof value === 'function';
|
|
189
59
|
function createLifoList() {
|
|
190
60
|
const list = createSet();
|
|
191
61
|
const push = (item) => {
|
|
192
|
-
if (
|
|
62
|
+
if (!isNonPrimitive(item)) {
|
|
193
63
|
throw createError('A lifo list only supports non-primitive values');
|
|
194
64
|
}
|
|
195
65
|
list.add(item);
|
|
@@ -226,21 +96,6 @@ var HyperfrontendListUtils = (function (exports) {
|
|
|
226
96
|
return freeze(result);
|
|
227
97
|
}
|
|
228
98
|
|
|
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
99
|
function createRange(start, end) {
|
|
245
100
|
const range = [];
|
|
246
101
|
for (let i = start; i <= end; i += 1) {
|
|
@@ -249,42 +104,16 @@ var HyperfrontendListUtils = (function (exports) {
|
|
|
249
104
|
return range;
|
|
250
105
|
}
|
|
251
106
|
|
|
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
107
|
function createValuePicker(values) {
|
|
269
108
|
if (!values || !values.length) {
|
|
270
109
|
throw createError('Expected values not to be an empty list.');
|
|
271
110
|
}
|
|
272
111
|
let index = -1;
|
|
273
|
-
/**
|
|
274
|
-
* Returns the current value without advancing the picker.
|
|
275
|
-
*
|
|
276
|
-
* @returns The current value in the list
|
|
277
|
-
*/
|
|
278
112
|
function current() {
|
|
279
113
|
if (index < 0)
|
|
280
114
|
index = 0;
|
|
281
115
|
return values[index];
|
|
282
116
|
}
|
|
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
117
|
function next() {
|
|
289
118
|
if (index < 0)
|
|
290
119
|
return current();
|
|
@@ -297,53 +126,15 @@ var HyperfrontendListUtils = (function (exports) {
|
|
|
297
126
|
};
|
|
298
127
|
}
|
|
299
128
|
|
|
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
129
|
const getLastKeyInMap = (map) => {
|
|
317
130
|
const items = [...map.keys()];
|
|
318
131
|
return items[items.length - 1];
|
|
319
132
|
};
|
|
320
133
|
|
|
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
134
|
function nonEmptyStrings(values) {
|
|
333
135
|
return values.filter((value) => ![undefined, null, ''].includes(value) && value.trim() !== '');
|
|
334
136
|
}
|
|
335
137
|
|
|
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
138
|
function uniqueStrings(values) {
|
|
348
139
|
return from(createSet(values));
|
|
349
140
|
}
|
package/bundle/index.iife.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var HyperfrontendListUtils=function(
|
|
1
|
+
var HyperfrontendListUtils=function(t){"use strict";const e=globalThis.Array.from,n=globalThis.Error,r=globalThis.Reflect,o=(t,e)=>r.construct(n,[t,e]),i=globalThis.Object.freeze,l=globalThis.Set,s=globalThis.Reflect,a=t=>s.construct(l,t?[t]:[]);return t.createFifoList=function(){const t=a();return i({push:e=>{if(("object"!=typeof(n=e)||null===n)&&"function"!=typeof n)throw o("A fifo list only supports non-primitive values");var n;if(t.has(e))throw o("Cannot push an item that already exists in the list");t.add(e)},pull:()=>{const e=t.values().next().value;return t.delete(e),e},map:n=>e(t.values()).map(n),forEach:e=>{t.forEach(e)},remove:e=>t.delete(e),has:e=>t.has(e),size:()=>t.size,clear:()=>t.clear()})},t.createLifoList=function(){const t=a();return i({push:e=>{if(("object"!=typeof(n=e)||null===n)&&"function"!=typeof n)throw o("A lifo list only supports non-primitive values");var n;t.add(e)},pull:()=>{const n=e(t).pop();return n&&t.delete(n),n},map:n=>e(t).map(n),forEach:e=>{t.forEach(e)},remove:e=>t.delete(e),has:e=>t.has(e),size:()=>t.size,clear:()=>{t.clear()}})},t.createRange=function(t,e){const n=[];for(let r=t;r<=e;r+=1)n.push(r);return n},t.createValuePicker=function(t){if(!t||!t.length)throw o("Expected values not to be an empty list.");let e=-1;function n(){return e<0&&(e=0),t[e]}return{current:n,next:function(){return e<0?n():(e=++e<t.length?e:0,t[e])}}},t.getLastKeyInMap=t=>{const e=[...t.keys()];return e[e.length-1]},t.nonEmptyStrings=function(t){return t.filter(t=>![void 0,null,""].includes(t)&&""!==t.trim())},t.uniqueStrings=function(t){return e(a(t))},t}({});
|