@hyperfrontend/questions 0.2.1 → 0.3.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 +14 -0
- package/FUNDING.md +1 -1
- package/README.md +50 -31
- package/SECURITY.md +50 -15
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.cjs.js +6 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/error/index.esm.js +5 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.cjs.js +4 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/math/index.esm.js +3 -1
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/number/index.cjs.js +5 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/number/index.esm.js +4 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.cjs.js +2 -0
- package/_dependencies/@hyperfrontend/immutable-api-utils/built-in-copy/promise/index.esm.js +2 -1
- package/index.cjs.js +971 -280
- package/index.d.ts +18 -10
- package/index.esm.js +968 -277
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.3.1](https://github.com/AndrewRedican/hyperfrontend/compare/135d10985b6f139f816af06c6e3996abf338d21d...adf0a4f77dece2be855e7ab88185a4fe84e7b16b) - 2026-09-14
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- reject a choice prompt no keypress could resolve
|
|
10
|
+
- submit the text before a trailing line ending in the same chunk
|
|
11
|
+
- cancel a waiting prompt when the input ends
|
|
12
|
+
|
|
13
|
+
## [0.3.0](https://github.com/AndrewRedican/hyperfrontend/compare/5f116abb8ba6355dfb283fa03b7481e5eb029480...7e783d3d9e6b6b3a42d226b44256b943958a5813) - 2026-08-04
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
|
|
17
|
+
- support clipboard paste and resize-aware redraw
|
|
18
|
+
|
|
5
19
|
## [0.2.1](https://github.com/AndrewRedican/hyperfrontend/compare/926d40cab470e589fc30a1ef3a61ca764ff047bd...466c0388c4cd516b9c704214140b4df1004098e6) - 2026-06-23
|
|
6
20
|
|
|
7
21
|
### Other
|
package/FUNDING.md
CHANGED
|
@@ -38,7 +38,7 @@ GitHub Sponsors benefits:
|
|
|
38
38
|
|
|
39
39
|
You can also make a one-time contribution:
|
|
40
40
|
|
|
41
|
-
**[
|
|
41
|
+
**[Make a one-time donation via GitHub Sponsors](https://github.com/sponsors/AndrewRedican)**
|
|
42
42
|
|
|
43
43
|
### 🏢 Corporate Sponsorship
|
|
44
44
|
|
package/README.md
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://www.hyperfrontend.dev/docs/libraries/questions/">
|
|
3
|
+
<img width="640" height="180" src="https://www.hyperfrontend.dev/media/banner-questions/banner.gif" alt="@hyperfrontend/questions">
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
2
6
|
|
|
3
7
|
<p align="center">
|
|
4
8
|
<a href="https://github.com/AndrewRedican/hyperfrontend/actions/workflows/ci-lib-questions.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/questions/">
|
|
42
|
+
<img width="640" height="360" src="https://www.hyperfrontend.dev/media/questions-prompt/hero.gif" alt="A multiselect prompt being programmed on the left and answered on the right, ending in a submitted result object">
|
|
43
|
+
</a>
|
|
44
|
+
</p>
|
|
45
|
+
|
|
36
46
|
Terminal prompting library with composable, functional API for text, select, confirm, and multiselect prompts
|
|
37
47
|
|
|
38
48
|
• 👉 See [**documentation**](https://www.hyperfrontend.dev/docs/libraries/questions/)
|
|
49
|
+
• 👉 See [**guides & tutorials**](https://www.hyperfrontend.dev/docs/guides/?package=%40hyperfrontend%2Fquestions)
|
|
39
50
|
|
|
40
51
|
## What is @hyperfrontend/questions?
|
|
41
52
|
|
|
@@ -43,28 +54,30 @@ A terminal prompting library built on functional programming principles. Create
|
|
|
43
54
|
|
|
44
55
|
### Key Features
|
|
45
56
|
|
|
46
|
-
- **Pure Functions
|
|
47
|
-
- **Composable API
|
|
48
|
-
- **Type-Safe
|
|
49
|
-
- **Zero External Dependencies
|
|
50
|
-
- **Searchable Multiselect
|
|
57
|
+
- **Pure Functions**: Every prompt is a pure function returning `Promise<PromptOutcome<T>>`, making results predictable and easily testable
|
|
58
|
+
- **[Composable API](https://www.hyperfrontend.dev/docs/guides/build-a-setup-wizard-for-your-cli/)**: Build complex interactive flows by combining simple prompt functions
|
|
59
|
+
- **[Type-Safe](https://www.hyperfrontend.dev/docs/libraries/questions/#api-PromptOutcome)**: Full TypeScript support with discriminated unions for prompt outcomes
|
|
60
|
+
- **Zero External Dependencies**: Uses only Node.js built-ins and `@hyperfrontend` utilities
|
|
61
|
+
- **[Searchable Multiselect](https://www.hyperfrontend.dev/docs/libraries/questions/#api-multiselect)**: Type-to-filter functionality for large option lists
|
|
62
|
+
- **Clipboard Paste**: Bracketed paste mode on TTYs (with a multi-character-chunk fallback elsewhere); pasted text is sanitized, and a bracketed paste never auto-submits
|
|
63
|
+
- **End of input is a cancellation**: when the input stream ends, a waiting prompt resolves as cancelled instead of waiting for a key that can never arrive
|
|
64
|
+
- **Unanswerable configurations throw**: a choice prompt with no choices, or a starting index outside its list, rejects with an error rather than painting a frame nothing can answer
|
|
65
|
+
- **Resize-Aware Rendering**: Prompts hard-wrap to the terminal width and repaint on resize, preserving value, cursor, selection, and validation state
|
|
51
66
|
|
|
52
67
|
### Architecture Highlights
|
|
53
68
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
- **
|
|
57
|
-
- **Explicit Outcomes** — Prompts return either `{ result: 'submitted', value: T }` or `{ result: 'cancelled', value: undefined }`
|
|
58
|
-
- **Terminal Abstraction** — Low-level I/O is encapsulated in a `Terminal` interface for testability
|
|
69
|
+
- **Explicit outcomes**: prompts resolve to either `{ result: 'submitted', value: T }` or `{ result: 'cancelled', value: undefined }`, so Ctrl+C is an ordinary branch to handle rather than a rejection to catch
|
|
70
|
+
- **Terminal state is restored**: raw mode is taken once for the whole prompt session and given back when it closes, on cancel as well as on submit
|
|
71
|
+
- **Rendering assumptions**: repainting on resize assumes a reflowing terminal, and display width is counted in code points, so east-asian double-width characters are out of scope
|
|
59
72
|
|
|
60
73
|
## Why Use @hyperfrontend/questions?
|
|
61
74
|
|
|
62
75
|
When building CLI tools, you need interactive prompts that are:
|
|
63
76
|
|
|
64
|
-
1. **Predictable
|
|
65
|
-
2. **Composable
|
|
66
|
-
3. **Cancellable
|
|
67
|
-
4. **Lightweight
|
|
77
|
+
1. **Predictable**: Know exactly what a prompt returns, always
|
|
78
|
+
2. **Composable**: Chain prompts without callback hell
|
|
79
|
+
3. **Cancellable**: Handle Ctrl+C gracefully with structured cancellation
|
|
80
|
+
4. **Lightweight**: No large dependency trees for simple prompts
|
|
68
81
|
|
|
69
82
|
This library provides all four while staying true to functional programming principles.
|
|
70
83
|
|
|
@@ -89,7 +102,7 @@ if (nameResult.result === PromptResult.Submitted) {
|
|
|
89
102
|
console.log(`Hello, ${nameResult.value}!`)
|
|
90
103
|
}
|
|
91
104
|
|
|
92
|
-
// Text input with a live label
|
|
105
|
+
// Text input with a live label; `renderMessage` is recomputed on every keystroke
|
|
93
106
|
import { style } from '@hyperfrontend/questions'
|
|
94
107
|
|
|
95
108
|
await text({
|
|
@@ -131,28 +144,34 @@ const featuresResult = await multiselect({
|
|
|
131
144
|
|
|
132
145
|
## API Overview
|
|
133
146
|
|
|
134
|
-
|
|
135
|
-
| -------------- | ----------------------------------------------------------------------------- |
|
|
136
|
-
| `text` | Free-form text input with optional validation and live-updating labels |
|
|
137
|
-
| `confirm` | Yes/no confirmation prompt |
|
|
138
|
-
| `select` | Single selection from a list of choices |
|
|
139
|
-
| `multiselect` | Multiple selections with optional search |
|
|
140
|
-
| `style` | ANSI colour helpers (`green`, `yellow`, `red`, `cyan`, `bold`, `dim`, `gray`) |
|
|
141
|
-
| `PromptResult` | Discriminated union: `'submitted' \| 'cancelled'` |
|
|
142
|
-
|
|
143
|
-
All prompts return `Promise<PromptOutcome<T>>` where:
|
|
147
|
+
Four prompts, one shape. [`text`](https://www.hyperfrontend.dev/docs/libraries/questions/#api-text), [`confirm`](https://www.hyperfrontend.dev/docs/libraries/questions/#api-confirm), [`select`](https://www.hyperfrontend.dev/docs/libraries/questions/#api-select) and [`multiselect`](https://www.hyperfrontend.dev/docs/libraries/questions/#api-multiselect) each take a config object and resolve to the same discriminated union, so the code that reads an answer is the same code whichever question asked it:
|
|
144
148
|
|
|
145
149
|
```typescript
|
|
146
150
|
type PromptOutcome<T> = { result: 'submitted'; value: T } | { result: 'cancelled'; value: undefined }
|
|
147
151
|
```
|
|
148
152
|
|
|
153
|
+
Two things sit beside them. [`style`](https://www.hyperfrontend.dev/docs/libraries/questions/#api-style) is the ANSI colour helper the prompts use on their own labels, exposed so yours can match. And every config takes [`input`](https://www.hyperfrontend.dev/docs/libraries/questions/#api-PromptConfig-prop-input) and [`output`](https://www.hyperfrontend.dev/docs/libraries/questions/#api-PromptConfig-prop-output) streams, which is what makes a prompt testable without a TTY: hand it a pair of `PassThrough`s, write keystrokes into one and read frames out of the other.
|
|
154
|
+
|
|
155
|
+
Every config, option and outcome type is in the [API reference](https://www.hyperfrontend.dev/docs/libraries/questions/#api-reference).
|
|
156
|
+
|
|
149
157
|
## Compatibility
|
|
150
158
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
159
|
+
<p align="center">
|
|
160
|
+
<a href="https://www.hyperfrontend.dev/docs/libraries/questions/#compatibility">
|
|
161
|
+
<img width="640" height="150" src="https://www.hyperfrontend.dev/media/runtimes-questions/runtimes.png" alt="Runs in Node.js 18 or later; not a target for evergreen browsers and web workers">
|
|
162
|
+
</a>
|
|
163
|
+
</p>
|
|
164
|
+
|
|
165
|
+
Prompts run on a TTY, where the session redraws when the terminal is resized, and on non-TTY streams such as tests and pipes. On TTY inputs a prompt session enables bracketed paste mode (`ESC[?2004h`) and restores it on close; terminals without bracketed paste still paste correctly because multi-character input chunks are treated as pastes. Single-line prompts collapse pasted newlines into spaces, so a bracketed paste can never submit a value. Where bracketed paste is unavailable, a chunk that ends in a line ending submits the text before it, which is how a line-buffered reader behaves and is what makes piped input work; line endings in the middle of such a chunk stay data.
|
|
166
|
+
|
|
167
|
+
### Output Formats
|
|
168
|
+
|
|
169
|
+
| Format | File | Tree-Shakeable |
|
|
170
|
+
| ------ | -------------- | :------------: |
|
|
171
|
+
| ESM | `index.esm.js` | ✅ |
|
|
172
|
+
| CJS | `index.cjs.js` | ❌ |
|
|
173
|
+
|
|
174
|
+
No browser bundle is published: the library drives a terminal, so an IIFE or UMD build would have nothing to run against.
|
|
156
175
|
|
|
157
176
|
## License
|
|
158
177
|
|
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.
|
|
@@ -3,4 +3,6 @@
|
|
|
3
3
|
const _Promise = globalThis.Promise;
|
|
4
4
|
const _Reflect = globalThis.Reflect;
|
|
5
5
|
const createPromise = (executor) => _Reflect.construct(_Promise, [executor]);
|
|
6
|
+
const promiseResolve = _Promise.resolve.bind(_Promise);
|
|
6
7
|
exports.createPromise = createPromise;
|
|
8
|
+
exports.promiseResolve = promiseResolve;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const _Promise = globalThis.Promise;
|
|
2
2
|
const _Reflect = globalThis.Reflect;
|
|
3
3
|
const createPromise = (executor) => _Reflect.construct(_Promise, [executor]);
|
|
4
|
+
const promiseResolve = _Promise.resolve.bind(_Promise);
|
|
4
5
|
|
|
5
|
-
export { createPromise };
|
|
6
|
+
export { createPromise, promiseResolve };
|