This commit is contained in:
CHEVALLIER Abel
2025-11-13 16:23:22 +01:00
parent de9c515a47
commit cb235644dc
34924 changed files with 3811102 additions and 0 deletions

21
node_modules/@angular/animations/LICENSE generated vendored Executable file
View File

@@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2010-2025 Google LLC. https://angular.dev/license
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

7
node_modules/@angular/animations/README.md generated vendored Executable file
View File

@@ -0,0 +1,7 @@
# Angular
The sources for this package are in the main [Angular](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo.
Usage information and reference details can be found in [Angular documentation](https://angular.dev/overview).
License: MIT

69
node_modules/@angular/animations/animation_driver.d.d.ts generated vendored Executable file
View File

@@ -0,0 +1,69 @@
/**
* @license Angular v20.3.11
* (c) 2010-2025 Google LLC. https://angular.dev/
* License: MIT
*/
import * as i0 from '@angular/core';
import { AnimationPlayer } from './animation_player.d.js';
/**
* @publicApi
*
* @deprecated 20.2 Use `animate.enter` or `animate.leave` instead. Intent to remove in v23
*
* `AnimationDriver` implentation for Noop animations
*/
declare class NoopAnimationDriver implements AnimationDriver {
/**
* @returns Whether `prop` is a valid CSS property
*/
validateStyleProperty(prop: string): boolean;
/**
*
* @returns Whether elm1 contains elm2.
*/
containsElement(elm1: any, elm2: any): boolean;
/**
* @returns Rhe parent of the given element or `null` if the element is the `document`
*/
getParentElement(element: unknown): unknown;
/**
* @returns The result of the query selector on the element. The array will contain up to 1 item
* if `multi` is `false`.
*/
query(element: any, selector: string, multi: boolean): any[];
/**
* @returns The `defaultValue` or empty string
*/
computeStyle(element: any, prop: string, defaultValue?: string): string;
/**
* @returns An `NoopAnimationPlayer`
*/
animate(element: any, keyframes: Array<Map<string, string | number>>, duration: number, delay: number, easing: string, previousPlayers?: any[], scrubberAccessRequested?: boolean): AnimationPlayer;
static ɵfac: i0.ɵɵFactoryDeclaration<NoopAnimationDriver, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<NoopAnimationDriver>;
}
/**
* @publicApi
*
* @deprecated 20.2 Use `animate.enter` or `animate.leave` instead. Intent to remove in v23
*/
declare abstract class AnimationDriver {
/**
* @deprecated Use the NoopAnimationDriver class.
*/
static NOOP: AnimationDriver;
abstract validateStyleProperty(prop: string): boolean;
abstract validateAnimatableStyleProperty?: (prop: string) => boolean;
abstract containsElement(elm1: any, elm2: any): boolean;
/**
* Obtains the parent element, if any. `null` is returned if the element does not have a parent.
*/
abstract getParentElement(element: unknown): unknown;
abstract query(element: any, selector: string, multi: boolean): any[];
abstract computeStyle(element: any, prop: string, defaultValue?: string): string;
abstract animate(element: any, keyframes: Array<Map<string, string | number>>, duration: number, delay: number, easing?: string | null, previousPlayers?: any[], scrubberAccessRequested?: boolean): any;
}
export { AnimationDriver, NoopAnimationDriver };

1477
node_modules/@angular/animations/animation_player.d.d.ts generated vendored Executable file

File diff suppressed because it is too large Load Diff

275
node_modules/@angular/animations/browser/index.d.ts generated vendored Executable file
View File

@@ -0,0 +1,275 @@
/**
* @license Angular v20.3.11
* (c) 2010-2025 Google LLC. https://angular.dev/
* License: MIT
*/
import { AnimationDriver } from '../animation_driver.d.js';
export { NoopAnimationDriver } from '../animation_driver.d.js';
import { AnimationTriggerMetadata, AnimationPlayer, ɵStyleDataMap as _StyleDataMap, AnimationMetadata, AnimationOptions, ɵStyleData as _StyleData } from '../animation_player.d.js';
import { Renderer2, ɵAnimationRendererType as _AnimationRendererType, RendererStyleFlags2, ListenerOptions, RendererFactory2, NgZone, RendererType2 } from '@angular/core';
declare abstract class AnimationStyleNormalizer {
abstract normalizePropertyName(propertyName: string, errors: Error[]): string;
abstract normalizeStyleValue(userProvidedProperty: string, normalizedProperty: string, value: string | number, errors: Error[]): string;
}
declare class NoopAnimationStyleNormalizer {
normalizePropertyName(propertyName: string, errors: Error[]): string;
normalizeStyleValue(userProvidedProperty: string, normalizedProperty: string, value: string | number, errors: Error[]): string;
}
declare class AnimationEngine {
private _driver;
private _normalizer;
private _transitionEngine;
private _timelineEngine;
private _triggerCache;
onRemovalComplete: (element: any, context: any) => void;
constructor(doc: Document, _driver: AnimationDriver, _normalizer: AnimationStyleNormalizer);
registerTrigger(componentId: string, namespaceId: string, hostElement: any, name: string, metadata: AnimationTriggerMetadata): void;
register(namespaceId: string, hostElement: any): void;
destroy(namespaceId: string, context: any): void;
onInsert(namespaceId: string, element: any, parent: any, insertBefore: boolean): void;
onRemove(namespaceId: string, element: any, context: any): void;
disableAnimations(element: any, disable: boolean): void;
process(namespaceId: string, element: any, property: string, value: any): void;
listen(namespaceId: string, element: any, eventName: string, eventPhase: string, callback: (event: any) => any): () => any;
flush(microtaskId?: number): void;
get players(): AnimationPlayer[];
whenRenderingDone(): Promise<any>;
afterFlushAnimationsDone(cb: VoidFunction): void;
}
declare function createEngine(type: 'animations' | 'noop', doc: Document): AnimationEngine;
declare const enum AnimationTransitionInstructionType {
TransitionAnimation = 0,
TimelineAnimation = 1
}
interface AnimationEngineInstruction {
type: AnimationTransitionInstructionType;
}
interface AnimationTimelineInstruction extends AnimationEngineInstruction {
element: any;
keyframes: Array<_StyleDataMap>;
preStyleProps: string[];
postStyleProps: string[];
duration: number;
delay: number;
totalTime: number;
easing: string | null;
stretchStartingKeyframe?: boolean;
subTimeline: boolean;
}
declare class ElementInstructionMap {
private _map;
get(element: any): AnimationTimelineInstruction[];
append(element: any, instructions: AnimationTimelineInstruction[]): void;
has(element: any): boolean;
clear(): void;
}
declare class Animation$1 {
private _driver;
private _animationAst;
constructor(_driver: AnimationDriver, input: AnimationMetadata | AnimationMetadata[]);
buildTimelines(element: any, startingStyles: _StyleDataMap | Array<_StyleDataMap>, destinationStyles: _StyleDataMap | Array<_StyleDataMap>, options: AnimationOptions, subInstructions?: ElementInstructionMap): AnimationTimelineInstruction[];
}
declare class WebAnimationsStyleNormalizer extends AnimationStyleNormalizer {
normalizePropertyName(propertyName: string, errors: Error[]): string;
normalizeStyleValue(userProvidedProperty: string, normalizedProperty: string, value: string | number, errors: Error[]): string;
}
type AnimationFactoryWithListenerCallback = RendererFactory2 & {
scheduleListenerCallback: (count: number, fn: (e: any) => any, data: any) => void;
};
declare class BaseAnimationRenderer implements Renderer2 {
protected namespaceId: string;
delegate: Renderer2;
engine: AnimationEngine;
private _onDestroy?;
readonly ɵtype: _AnimationRendererType.Regular;
constructor(namespaceId: string, delegate: Renderer2, engine: AnimationEngine, _onDestroy?: (() => void) | undefined);
get data(): {
[key: string]: any;
};
destroyNode(node: any): void;
destroy(): void;
createElement(name: string, namespace?: string | null | undefined): any;
createComment(value: string): any;
createText(value: string): any;
appendChild(parent: any, newChild: any): void;
insertBefore(parent: any, newChild: any, refChild: any, isMove?: boolean): void;
removeChild(parent: any, oldChild: any, isHostElement?: boolean, requireSynchronousElementRemoval?: boolean): void;
selectRootElement(selectorOrNode: any, preserveContent?: boolean): any;
parentNode(node: any): any;
nextSibling(node: any): any;
setAttribute(el: any, name: string, value: string, namespace?: string | null | undefined): void;
removeAttribute(el: any, name: string, namespace?: string | null | undefined): void;
addClass(el: any, name: string): void;
removeClass(el: any, name: string): void;
setStyle(el: any, style: string, value: any, flags?: RendererStyleFlags2 | undefined): void;
removeStyle(el: any, style: string, flags?: RendererStyleFlags2 | undefined): void;
setProperty(el: any, name: string, value: any): void;
setValue(node: any, value: string): void;
listen(target: any, eventName: string, callback: (event: any) => boolean | void, options?: ListenerOptions): () => void;
protected disableAnimations(element: any, value: boolean): void;
}
declare class AnimationRenderer extends BaseAnimationRenderer implements Renderer2 {
factory: AnimationFactoryWithListenerCallback;
constructor(factory: AnimationFactoryWithListenerCallback, namespaceId: string, delegate: Renderer2, engine: AnimationEngine, onDestroy?: () => void);
setProperty(el: any, name: string, value: any): void;
listen(target: 'window' | 'document' | 'body' | any, eventName: string, callback: (event: any) => any, options?: ListenerOptions): () => void;
}
declare class AnimationRendererFactory implements RendererFactory2 {
private delegate;
private engine;
private _zone;
private _currentId;
private _microtaskId;
private _animationCallbacksBuffer;
private _rendererCache;
private _cdRecurDepth;
constructor(delegate: RendererFactory2, engine: AnimationEngine, _zone: NgZone);
createRenderer(hostElement: any, type: RendererType2): BaseAnimationRenderer;
begin(): void;
private _scheduleCountTask;
end(): void;
whenRenderingDone(): Promise<any>;
/**
* Used during HMR to clear any cached data about a component.
* @param componentId ID of the component that is being replaced.
*/
protected componentReplaced(componentId: string): void;
}
declare function getParentElement(element: any): unknown | null;
declare function validateStyleProperty(prop: string): boolean;
declare function validateWebAnimatableStyleProperty(prop: string): boolean;
declare function containsElement(elm1: any, elm2: any): boolean;
declare function invokeQuery(element: any, selector: string, multi: boolean): any[];
declare class WebAnimationsDriver implements AnimationDriver {
validateStyleProperty(prop: string): boolean;
validateAnimatableStyleProperty(prop: string): boolean;
containsElement(elm1: any, elm2: any): boolean;
getParentElement(element: unknown): unknown;
query(element: any, selector: string, multi: boolean): any[];
computeStyle(element: any, prop: string, defaultValue?: string): string;
animate(element: any, keyframes: Array<Map<string, string | number>>, duration: number, delay: number, easing: string, previousPlayers?: AnimationPlayer[]): AnimationPlayer;
}
/**
* Designed to be executed during a keyframe-based animation to apply any special-cased styles.
*
* When started (when the `start()` method is run) then the provided `startStyles`
* will be applied. When finished (when the `finish()` method is called) the
* `endStyles` will be applied as well any any starting styles. Finally when
* `destroy()` is called then all styles will be removed.
*/
declare class SpecialCasedStyles {
private _element;
private _startStyles;
private _endStyles;
static initialStylesByElement: WeakMap<any, _StyleDataMap>;
private _state;
private _initialStyles;
constructor(_element: any, _startStyles: _StyleDataMap | null, _endStyles: _StyleDataMap | null);
start(): void;
finish(): void;
destroy(): void;
}
declare class WebAnimationsPlayer implements AnimationPlayer {
element: any;
keyframes: Array<_StyleDataMap>;
options: {
[key: string]: string | number;
};
private _specialStyles?;
private _onDoneFns;
private _onStartFns;
private _onDestroyFns;
private _duration;
private _delay;
private _initialized;
private _finished;
private _started;
private _destroyed;
private _finalKeyframe?;
private _originalOnDoneFns;
private _originalOnStartFns;
domPlayer: Animation | null;
time: number;
parentPlayer: AnimationPlayer | null;
currentSnapshot: _StyleDataMap;
constructor(element: any, keyframes: Array<_StyleDataMap>, options: {
[key: string]: string | number;
}, _specialStyles?: (SpecialCasedStyles | null) | undefined);
private _onFinish;
init(): void;
private _buildPlayer;
private _preparePlayerBeforeStart;
private _convertKeyframesToObject;
onStart(fn: () => void): void;
onDone(fn: () => void): void;
onDestroy(fn: () => void): void;
play(): void;
pause(): void;
finish(): void;
reset(): void;
private _resetDomPlayerState;
restart(): void;
hasStarted(): boolean;
destroy(): void;
setPosition(p: number): void;
getPosition(): number;
get totalTime(): number;
beforeDestroy(): void;
}
declare const ENTER_CLASSNAME = "ng-enter";
declare const LEAVE_CLASSNAME = "ng-leave";
declare function normalizeKeyframes(keyframes: Array<_StyleData> | Array<_StyleDataMap>): Array<_StyleDataMap>;
declare function camelCaseToDashCase(input: string): string;
declare function allowPreviousPlayerStylesMerge(duration: number, delay: number): boolean;
declare class TransitionAnimationPlayer implements AnimationPlayer {
namespaceId: string;
triggerName: string;
element: any;
private _player;
private _containsRealPlayer;
private _queuedCallbacks;
readonly destroyed = false;
parentPlayer: AnimationPlayer | null;
markedForDestroy: boolean;
disabled: boolean;
readonly queued: boolean;
readonly totalTime: number;
constructor(namespaceId: string, triggerName: string, element: any);
setRealPlayer(player: AnimationPlayer): void;
getRealPlayer(): AnimationPlayer;
overrideTotalTime(totalTime: number): void;
syncPlayerEvents(player: AnimationPlayer): void;
private _queueEvent;
onDone(fn: () => void): void;
onStart(fn: () => void): void;
onDestroy(fn: () => void): void;
init(): void;
hasStarted(): boolean;
play(): void;
pause(): void;
restart(): void;
finish(): void;
destroy(): void;
reset(): void;
setPosition(p: number): void;
getPosition(): number;
}
export { AnimationDriver, Animation$1 as ɵAnimation, AnimationEngine as ɵAnimationEngine, AnimationRenderer as ɵAnimationRenderer, AnimationRendererFactory as ɵAnimationRendererFactory, AnimationStyleNormalizer as ɵAnimationStyleNormalizer, BaseAnimationRenderer as ɵBaseAnimationRenderer, ENTER_CLASSNAME as ɵENTER_CLASSNAME, LEAVE_CLASSNAME as ɵLEAVE_CLASSNAME, NoopAnimationStyleNormalizer as ɵNoopAnimationStyleNormalizer, TransitionAnimationPlayer as ɵTransitionAnimationPlayer, WebAnimationsDriver as ɵWebAnimationsDriver, WebAnimationsPlayer as ɵWebAnimationsPlayer, WebAnimationsStyleNormalizer as ɵWebAnimationsStyleNormalizer, allowPreviousPlayerStylesMerge as ɵallowPreviousPlayerStylesMerge, camelCaseToDashCase as ɵcamelCaseToDashCase, containsElement as ɵcontainsElement, createEngine as ɵcreateEngine, getParentElement as ɵgetParentElement, invokeQuery as ɵinvokeQuery, normalizeKeyframes as ɵnormalizeKeyframes, validateStyleProperty as ɵvalidateStyleProperty, validateWebAnimatableStyleProperty as ɵvalidateWebAnimatableStyleProperty };

53
node_modules/@angular/animations/browser/testing/index.d.ts generated vendored Executable file
View File

@@ -0,0 +1,53 @@
/**
* @license Angular v20.3.11
* (c) 2010-2025 Google LLC. https://angular.dev/
* License: MIT
*/
import { AnimationPlayer, ɵStyleDataMap as _StyleDataMap, NoopAnimationPlayer } from '../../animation_player.d.js';
import { AnimationDriver } from '../../animation_driver.d.js';
import '@angular/core';
/**
* @publicApi
*
* @deprecated 20.2 Use `animate.enter` or `animate.leave` instead. Intent to remove in v23
*/
declare class MockAnimationDriver implements AnimationDriver {
static log: AnimationPlayer[];
validateStyleProperty(prop: string): boolean;
validateAnimatableStyleProperty(prop: string): boolean;
containsElement(elm1: any, elm2: any): boolean;
getParentElement(element: unknown): unknown;
query(element: any, selector: string, multi: boolean): any[];
computeStyle(element: any, prop: string, defaultValue?: string): string;
animate(element: any, keyframes: Array<_StyleDataMap>, duration: number, delay: number, easing: string, previousPlayers?: any[]): MockAnimationPlayer;
}
/**
* @publicApi
*
* @deprecated 20.2 Use `animate.enter` or `animate.leave` instead. Intent to remove in v23
*/
declare class MockAnimationPlayer extends NoopAnimationPlayer {
element: any;
keyframes: Array<_StyleDataMap>;
duration: number;
delay: number;
easing: string;
previousPlayers: any[];
private __finished;
private __started;
previousStyles: _StyleDataMap;
private _onInitFns;
currentSnapshot: _StyleDataMap;
private _keyframes;
constructor(element: any, keyframes: Array<_StyleDataMap>, duration: number, delay: number, easing: string, previousPlayers: any[]);
reset(): void;
finish(): void;
destroy(): void;
play(): void;
hasStarted(): boolean;
beforeDestroy(): void;
}
export { MockAnimationDriver, MockAnimationPlayer };

212
node_modules/@angular/animations/fesm2022/animations.mjs generated vendored Executable file
View File

@@ -0,0 +1,212 @@
/**
* @license Angular v20.3.11
* (c) 2010-2025 Google LLC. https://angular.dev/
* License: MIT
*/
import * as i0 from '@angular/core';
import { inject, Injectable, ANIMATION_MODULE_TYPE, ɵRuntimeError as _RuntimeError, DOCUMENT, Inject, ViewEncapsulation } from '@angular/core';
import { sequence } from './private_export.mjs';
export { AUTO_STYLE, AnimationMetadataType, NoopAnimationPlayer, animate, animateChild, animation, group, keyframes, query, stagger, state, style, transition, trigger, useAnimation, AnimationGroupPlayer as ɵAnimationGroupPlayer, ɵPRE_STYLE } from './private_export.mjs';
/**
* An injectable service that produces an animation sequence programmatically within an
* Angular component or directive.
* Provided by the `BrowserAnimationsModule` or `NoopAnimationsModule`.
*
* @usageNotes
*
* To use this service, add it to your component or directive as a dependency.
* The service is instantiated along with your component.
*
* Apps do not typically need to create their own animation players, but if you
* do need to, follow these steps:
*
* 1. Use the <code>[AnimationBuilder.build](api/animations/AnimationBuilder#build)()</code> method
* to create a programmatic animation. The method returns an `AnimationFactory` instance.
*
* 2. Use the factory object to create an `AnimationPlayer` and attach it to a DOM element.
*
* 3. Use the player object to control the animation programmatically.
*
* For example:
*
* ```ts
* // import the service from BrowserAnimationsModule
* import {AnimationBuilder} from '@angular/animations';
* // require the service as a dependency
* class MyCmp {
* constructor(private _builder: AnimationBuilder) {}
*
* makeAnimation(element: any) {
* // first define a reusable animation
* const myAnimation = this._builder.build([
* style({ width: 0 }),
* animate(1000, style({ width: '100px' }))
* ]);
*
* // use the returned factory object to create a player
* const player = myAnimation.create(element);
*
* player.play();
* }
* }
* ```
*
* @publicApi
*
* @deprecated 20.2 Use `animate.enter` or `animate.leave` instead. Intent to remove in v23
*/
class AnimationBuilder {
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.11", ngImport: i0, type: AnimationBuilder, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.11", ngImport: i0, type: AnimationBuilder, providedIn: 'root', useFactory: () => inject(BrowserAnimationBuilder) });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.11", ngImport: i0, type: AnimationBuilder, decorators: [{
type: Injectable,
args: [{ providedIn: 'root', useFactory: () => inject(BrowserAnimationBuilder) }]
}] });
/**
* A factory object returned from the
* <code>[AnimationBuilder.build](api/animations/AnimationBuilder#build)()</code>
* method.
*
* @publicApi
*
* @deprecated 20.2 Use `animate.enter` or `animate.leave` instead. Intent to remove in v23
*/
class AnimationFactory {
}
class BrowserAnimationBuilder extends AnimationBuilder {
animationModuleType = inject(ANIMATION_MODULE_TYPE, { optional: true });
_nextAnimationId = 0;
_renderer;
constructor(rootRenderer, doc) {
super();
const typeData = {
id: '0',
encapsulation: ViewEncapsulation.None,
styles: [],
data: { animation: [] },
};
this._renderer = rootRenderer.createRenderer(doc.body, typeData);
if (this.animationModuleType === null && !isAnimationRenderer(this._renderer)) {
// We only support AnimationRenderer & DynamicDelegationRenderer for this AnimationBuilder
throw new _RuntimeError(3600 /* RuntimeErrorCode.BROWSER_ANIMATION_BUILDER_INJECTED_WITHOUT_ANIMATIONS */, (typeof ngDevMode === 'undefined' || ngDevMode) &&
'Angular detected that the `AnimationBuilder` was injected, but animation support was not enabled. ' +
'Please make sure that you enable animations in your application by calling `provideAnimations()` or `provideAnimationsAsync()` function.');
}
}
build(animation) {
const id = this._nextAnimationId;
this._nextAnimationId++;
const entry = Array.isArray(animation) ? sequence(animation) : animation;
issueAnimationCommand(this._renderer, null, id, 'register', [entry]);
return new BrowserAnimationFactory(id, this._renderer);
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.11", ngImport: i0, type: BrowserAnimationBuilder, deps: [{ token: i0.RendererFactory2 }, { token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.11", ngImport: i0, type: BrowserAnimationBuilder, providedIn: 'root' });
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.11", ngImport: i0, type: BrowserAnimationBuilder, decorators: [{
type: Injectable,
args: [{ providedIn: 'root' }]
}], ctorParameters: () => [{ type: i0.RendererFactory2 }, { type: Document, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }] });
class BrowserAnimationFactory extends AnimationFactory {
_id;
_renderer;
constructor(_id, _renderer) {
super();
this._id = _id;
this._renderer = _renderer;
}
create(element, options) {
return new RendererAnimationPlayer(this._id, element, options || {}, this._renderer);
}
}
class RendererAnimationPlayer {
id;
element;
_renderer;
parentPlayer = null;
_started = false;
constructor(id, element, options, _renderer) {
this.id = id;
this.element = element;
this._renderer = _renderer;
this._command('create', options);
}
_listen(eventName, callback) {
return this._renderer.listen(this.element, `@@${this.id}:${eventName}`, callback);
}
_command(command, ...args) {
issueAnimationCommand(this._renderer, this.element, this.id, command, args);
}
onDone(fn) {
this._listen('done', fn);
}
onStart(fn) {
this._listen('start', fn);
}
onDestroy(fn) {
this._listen('destroy', fn);
}
init() {
this._command('init');
}
hasStarted() {
return this._started;
}
play() {
this._command('play');
this._started = true;
}
pause() {
this._command('pause');
}
restart() {
this._command('restart');
}
finish() {
this._command('finish');
}
destroy() {
this._command('destroy');
}
reset() {
this._command('reset');
this._started = false;
}
setPosition(p) {
this._command('setPosition', p);
}
getPosition() {
return unwrapAnimationRenderer(this._renderer)?.engine?.players[this.id]?.getPosition() ?? 0;
}
totalTime = 0;
}
function issueAnimationCommand(renderer, element, id, command, args) {
renderer.setProperty(element, `@@${id}:${command}`, args);
}
/**
* The following 2 methods cannot reference their correct types (AnimationRenderer &
* DynamicDelegationRenderer) since this would introduce a import cycle.
*/
function unwrapAnimationRenderer(renderer) {
const type = renderer.ɵtype;
if (type === 0 /* AnimationRendererType.Regular */) {
return renderer;
}
else if (type === 1 /* AnimationRendererType.Delegated */) {
return renderer.animationRenderer;
}
return null;
}
function isAnimationRenderer(renderer) {
const type = renderer.ɵtype;
return type === 0 /* AnimationRendererType.Regular */ || type === 1 /* AnimationRendererType.Delegated */;
}
export { AnimationBuilder, AnimationFactory, sequence, BrowserAnimationBuilder as ɵBrowserAnimationBuilder };
//# sourceMappingURL=animations.mjs.map

File diff suppressed because one or more lines are too long

4204
node_modules/@angular/animations/fesm2022/browser.mjs generated vendored Executable file

File diff suppressed because it is too large Load Diff

1
node_modules/@angular/animations/fesm2022/browser.mjs.map generated vendored Executable file

File diff suppressed because one or more lines are too long

130
node_modules/@angular/animations/fesm2022/browser/testing.mjs generated vendored Executable file
View File

@@ -0,0 +1,130 @@
/**
* @license Angular v20.3.11
* (c) 2010-2025 Google LLC. https://angular.dev/
* License: MIT
*/
import { validateStyleProperty, camelCaseToDashCase, validateWebAnimatableStyleProperty, containsElement, getParentElement, invokeQuery, normalizeKeyframes$1 as normalizeKeyframes, allowPreviousPlayerStylesMerge } from '../util.mjs';
import { NoopAnimationPlayer, AUTO_STYLE } from '../private_export.mjs';
import '@angular/core';
/**
* @publicApi
*
* @deprecated 20.2 Use `animate.enter` or `animate.leave` instead. Intent to remove in v23
*/
class MockAnimationDriver {
static log = [];
validateStyleProperty(prop) {
return validateStyleProperty(prop);
}
validateAnimatableStyleProperty(prop) {
const cssProp = camelCaseToDashCase(prop);
return validateWebAnimatableStyleProperty(cssProp);
}
containsElement(elm1, elm2) {
return containsElement(elm1, elm2);
}
getParentElement(element) {
return getParentElement(element);
}
query(element, selector, multi) {
return invokeQuery(element, selector, multi);
}
computeStyle(element, prop, defaultValue) {
return defaultValue || '';
}
animate(element, keyframes, duration, delay, easing, previousPlayers = []) {
const player = new MockAnimationPlayer(element, keyframes, duration, delay, easing, previousPlayers);
MockAnimationDriver.log.push(player);
return player;
}
}
/**
* @publicApi
*
* @deprecated 20.2 Use `animate.enter` or `animate.leave` instead. Intent to remove in v23
*/
class MockAnimationPlayer extends NoopAnimationPlayer {
element;
keyframes;
duration;
delay;
easing;
previousPlayers;
__finished = false;
__started = false;
previousStyles = new Map();
_onInitFns = [];
currentSnapshot = new Map();
_keyframes = [];
constructor(element, keyframes, duration, delay, easing, previousPlayers) {
super(duration, delay);
this.element = element;
this.keyframes = keyframes;
this.duration = duration;
this.delay = delay;
this.easing = easing;
this.previousPlayers = previousPlayers;
this._keyframes = normalizeKeyframes(keyframes);
if (allowPreviousPlayerStylesMerge(duration, delay)) {
previousPlayers.forEach((player) => {
if (player instanceof MockAnimationPlayer) {
const styles = player.currentSnapshot;
styles.forEach((val, prop) => this.previousStyles.set(prop, val));
}
});
}
}
/** @internal */
onInit(fn) {
this._onInitFns.push(fn);
}
/** @internal */
init() {
super.init();
this._onInitFns.forEach((fn) => fn());
this._onInitFns = [];
}
reset() {
super.reset();
this.__started = false;
}
finish() {
super.finish();
this.__finished = true;
}
destroy() {
super.destroy();
this.__finished = true;
}
/** @internal */
triggerMicrotask() { }
play() {
super.play();
this.__started = true;
}
hasStarted() {
return this.__started;
}
beforeDestroy() {
const captures = new Map();
this.previousStyles.forEach((val, prop) => captures.set(prop, val));
if (this.hasStarted()) {
// when assembling the captured styles, it's important that
// we build the keyframe styles in the following order:
// {other styles within keyframes, ... previousStyles }
this._keyframes.forEach((kf) => {
for (let [prop, val] of kf) {
if (prop !== 'offset') {
captures.set(prop, this.__finished ? val : AUTO_STYLE);
}
}
});
}
this.currentSnapshot = captures;
}
}
export { MockAnimationDriver, MockAnimationPlayer };
//# sourceMappingURL=testing.mjs.map

File diff suppressed because one or more lines are too long

1208
node_modules/@angular/animations/fesm2022/private_export.mjs generated vendored Executable file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

697
node_modules/@angular/animations/fesm2022/util.mjs generated vendored Executable file
View File

@@ -0,0 +1,697 @@
/**
* @license Angular v20.3.11
* (c) 2010-2025 Google LLC. https://angular.dev/
* License: MIT
*/
import { AnimationGroupPlayer, NoopAnimationPlayer, AUTO_STYLE, ɵPRE_STYLE as _PRE_STYLE, AnimationMetadataType, sequence } from './private_export.mjs';
import { ɵRuntimeError as _RuntimeError } from '@angular/core';
const LINE_START = '\n - ';
function invalidTimingValue(exp) {
return new _RuntimeError(3000 /* RuntimeErrorCode.INVALID_TIMING_VALUE */, ngDevMode && `The provided timing value "${exp}" is invalid.`);
}
function negativeStepValue() {
return new _RuntimeError(3100 /* RuntimeErrorCode.NEGATIVE_STEP_VALUE */, ngDevMode && 'Duration values below 0 are not allowed for this animation step.');
}
function negativeDelayValue() {
return new _RuntimeError(3101 /* RuntimeErrorCode.NEGATIVE_DELAY_VALUE */, ngDevMode && 'Delay values below 0 are not allowed for this animation step.');
}
function invalidStyleParams(varName) {
return new _RuntimeError(3001 /* RuntimeErrorCode.INVALID_STYLE_PARAMS */, ngDevMode &&
`Unable to resolve the local animation param ${varName} in the given list of values`);
}
function invalidParamValue(varName) {
return new _RuntimeError(3003 /* RuntimeErrorCode.INVALID_PARAM_VALUE */, ngDevMode && `Please provide a value for the animation param ${varName}`);
}
function invalidNodeType(nodeType) {
return new _RuntimeError(3004 /* RuntimeErrorCode.INVALID_NODE_TYPE */, ngDevMode && `Unable to resolve animation metadata node #${nodeType}`);
}
function invalidCssUnitValue(userProvidedProperty, value) {
return new _RuntimeError(3005 /* RuntimeErrorCode.INVALID_CSS_UNIT_VALUE */, ngDevMode && `Please provide a CSS unit value for ${userProvidedProperty}:${value}`);
}
function invalidTrigger() {
return new _RuntimeError(3006 /* RuntimeErrorCode.INVALID_TRIGGER */, ngDevMode &&
"animation triggers cannot be prefixed with an `@` sign (e.g. trigger('@foo', [...]))");
}
function invalidDefinition() {
return new _RuntimeError(3007 /* RuntimeErrorCode.INVALID_DEFINITION */, ngDevMode && 'only state() and transition() definitions can sit inside of a trigger()');
}
function invalidState(metadataName, missingSubs) {
return new _RuntimeError(3008 /* RuntimeErrorCode.INVALID_STATE */, ngDevMode &&
`state("${metadataName}", ...) must define default values for all the following style substitutions: ${missingSubs.join(', ')}`);
}
function invalidStyleValue(value) {
return new _RuntimeError(3002 /* RuntimeErrorCode.INVALID_STYLE_VALUE */, ngDevMode && `The provided style string value ${value} is not allowed.`);
}
function invalidParallelAnimation(prop, firstStart, firstEnd, secondStart, secondEnd) {
return new _RuntimeError(3010 /* RuntimeErrorCode.INVALID_PARALLEL_ANIMATION */, ngDevMode &&
`The CSS property "${prop}" that exists between the times of "${firstStart}ms" and "${firstEnd}ms" is also being animated in a parallel animation between the times of "${secondStart}ms" and "${secondEnd}ms"`);
}
function invalidKeyframes() {
return new _RuntimeError(3011 /* RuntimeErrorCode.INVALID_KEYFRAMES */, ngDevMode && `keyframes() must be placed inside of a call to animate()`);
}
function invalidOffset() {
return new _RuntimeError(3012 /* RuntimeErrorCode.INVALID_OFFSET */, ngDevMode && `Please ensure that all keyframe offsets are between 0 and 1`);
}
function keyframeOffsetsOutOfOrder() {
return new _RuntimeError(3200 /* RuntimeErrorCode.KEYFRAME_OFFSETS_OUT_OF_ORDER */, ngDevMode && `Please ensure that all keyframe offsets are in order`);
}
function keyframesMissingOffsets() {
return new _RuntimeError(3202 /* RuntimeErrorCode.KEYFRAMES_MISSING_OFFSETS */, ngDevMode && `Not all style() steps within the declared keyframes() contain offsets`);
}
function invalidStagger() {
return new _RuntimeError(3013 /* RuntimeErrorCode.INVALID_STAGGER */, ngDevMode && `stagger() can only be used inside of query()`);
}
function invalidQuery(selector) {
return new _RuntimeError(3014 /* RuntimeErrorCode.INVALID_QUERY */, ngDevMode &&
`\`query("${selector}")\` returned zero elements. (Use \`query("${selector}", { optional: true })\` if you wish to allow this.)`);
}
function invalidExpression(expr) {
return new _RuntimeError(3015 /* RuntimeErrorCode.INVALID_EXPRESSION */, ngDevMode && `The provided transition expression "${expr}" is not supported`);
}
function invalidTransitionAlias(alias) {
return new _RuntimeError(3016 /* RuntimeErrorCode.INVALID_TRANSITION_ALIAS */, ngDevMode && `The transition alias value "${alias}" is not supported`);
}
function validationFailed(errors) {
return new _RuntimeError(3500 /* RuntimeErrorCode.VALIDATION_FAILED */, ngDevMode && `animation validation failed:\n${errors.map((err) => err.message).join('\n')}`);
}
function buildingFailed(errors) {
return new _RuntimeError(3501 /* RuntimeErrorCode.BUILDING_FAILED */, ngDevMode && `animation building failed:\n${errors.map((err) => err.message).join('\n')}`);
}
function triggerBuildFailed(name, errors) {
return new _RuntimeError(3404 /* RuntimeErrorCode.TRIGGER_BUILD_FAILED */, ngDevMode &&
`The animation trigger "${name}" has failed to build due to the following errors:\n - ${errors
.map((err) => err.message)
.join('\n - ')}`);
}
function animationFailed(errors) {
return new _RuntimeError(3502 /* RuntimeErrorCode.ANIMATION_FAILED */, ngDevMode &&
`Unable to animate due to the following errors:${LINE_START}${errors
.map((err) => err.message)
.join(LINE_START)}`);
}
function registerFailed(errors) {
return new _RuntimeError(3503 /* RuntimeErrorCode.REGISTRATION_FAILED */, ngDevMode &&
`Unable to build the animation due to the following errors: ${errors
.map((err) => err.message)
.join('\n')}`);
}
function missingOrDestroyedAnimation() {
return new _RuntimeError(3300 /* RuntimeErrorCode.MISSING_OR_DESTROYED_ANIMATION */, ngDevMode && "The requested animation doesn't exist or has already been destroyed");
}
function createAnimationFailed(errors) {
return new _RuntimeError(3504 /* RuntimeErrorCode.CREATE_ANIMATION_FAILED */, ngDevMode &&
`Unable to create the animation due to the following errors:${errors
.map((err) => err.message)
.join('\n')}`);
}
function missingPlayer(id) {
return new _RuntimeError(3301 /* RuntimeErrorCode.MISSING_PLAYER */, ngDevMode && `Unable to find the timeline player referenced by ${id}`);
}
function missingTrigger(phase, name) {
return new _RuntimeError(3302 /* RuntimeErrorCode.MISSING_TRIGGER */, ngDevMode &&
`Unable to listen on the animation trigger event "${phase}" because the animation trigger "${name}" doesn\'t exist!`);
}
function missingEvent(name) {
return new _RuntimeError(3303 /* RuntimeErrorCode.MISSING_EVENT */, ngDevMode &&
`Unable to listen on the animation trigger "${name}" because the provided event is undefined!`);
}
function unsupportedTriggerEvent(phase, name) {
return new _RuntimeError(3400 /* RuntimeErrorCode.UNSUPPORTED_TRIGGER_EVENT */, ngDevMode &&
`The provided animation trigger event "${phase}" for the animation trigger "${name}" is not supported!`);
}
function unregisteredTrigger(name) {
return new _RuntimeError(3401 /* RuntimeErrorCode.UNREGISTERED_TRIGGER */, ngDevMode && `The provided animation trigger "${name}" has not been registered!`);
}
function triggerTransitionsFailed(errors) {
return new _RuntimeError(3402 /* RuntimeErrorCode.TRIGGER_TRANSITIONS_FAILED */, ngDevMode &&
`Unable to process animations due to the following failed trigger transitions\n ${errors
.map((err) => err.message)
.join('\n')}`);
}
function transitionFailed(name, errors) {
return new _RuntimeError(3505 /* RuntimeErrorCode.TRANSITION_FAILED */, ngDevMode && `@${name} has failed due to:\n ${errors.map((err) => err.message).join('\n- ')}`);
}
/**
* Set of all animatable CSS properties
*
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties
*/
const ANIMATABLE_PROP_SET = new Set([
'-moz-outline-radius',
'-moz-outline-radius-bottomleft',
'-moz-outline-radius-bottomright',
'-moz-outline-radius-topleft',
'-moz-outline-radius-topright',
'-ms-grid-columns',
'-ms-grid-rows',
'-webkit-line-clamp',
'-webkit-text-fill-color',
'-webkit-text-stroke',
'-webkit-text-stroke-color',
'accent-color',
'all',
'backdrop-filter',
'background',
'background-color',
'background-position',
'background-size',
'block-size',
'border',
'border-block-end',
'border-block-end-color',
'border-block-end-width',
'border-block-start',
'border-block-start-color',
'border-block-start-width',
'border-bottom',
'border-bottom-color',
'border-bottom-left-radius',
'border-bottom-right-radius',
'border-bottom-width',
'border-color',
'border-end-end-radius',
'border-end-start-radius',
'border-image-outset',
'border-image-slice',
'border-image-width',
'border-inline-end',
'border-inline-end-color',
'border-inline-end-width',
'border-inline-start',
'border-inline-start-color',
'border-inline-start-width',
'border-left',
'border-left-color',
'border-left-width',
'border-radius',
'border-right',
'border-right-color',
'border-right-width',
'border-start-end-radius',
'border-start-start-radius',
'border-top',
'border-top-color',
'border-top-left-radius',
'border-top-right-radius',
'border-top-width',
'border-width',
'bottom',
'box-shadow',
'caret-color',
'clip',
'clip-path',
'color',
'column-count',
'column-gap',
'column-rule',
'column-rule-color',
'column-rule-width',
'column-width',
'columns',
'filter',
'flex',
'flex-basis',
'flex-grow',
'flex-shrink',
'font',
'font-size',
'font-size-adjust',
'font-stretch',
'font-variation-settings',
'font-weight',
'gap',
'grid-column-gap',
'grid-gap',
'grid-row-gap',
'grid-template-columns',
'grid-template-rows',
'height',
'inline-size',
'input-security',
'inset',
'inset-block',
'inset-block-end',
'inset-block-start',
'inset-inline',
'inset-inline-end',
'inset-inline-start',
'left',
'letter-spacing',
'line-clamp',
'line-height',
'margin',
'margin-block-end',
'margin-block-start',
'margin-bottom',
'margin-inline-end',
'margin-inline-start',
'margin-left',
'margin-right',
'margin-top',
'mask',
'mask-border',
'mask-position',
'mask-size',
'max-block-size',
'max-height',
'max-inline-size',
'max-lines',
'max-width',
'min-block-size',
'min-height',
'min-inline-size',
'min-width',
'object-position',
'offset',
'offset-anchor',
'offset-distance',
'offset-path',
'offset-position',
'offset-rotate',
'opacity',
'order',
'outline',
'outline-color',
'outline-offset',
'outline-width',
'padding',
'padding-block-end',
'padding-block-start',
'padding-bottom',
'padding-inline-end',
'padding-inline-start',
'padding-left',
'padding-right',
'padding-top',
'perspective',
'perspective-origin',
'right',
'rotate',
'row-gap',
'scale',
'scroll-margin',
'scroll-margin-block',
'scroll-margin-block-end',
'scroll-margin-block-start',
'scroll-margin-bottom',
'scroll-margin-inline',
'scroll-margin-inline-end',
'scroll-margin-inline-start',
'scroll-margin-left',
'scroll-margin-right',
'scroll-margin-top',
'scroll-padding',
'scroll-padding-block',
'scroll-padding-block-end',
'scroll-padding-block-start',
'scroll-padding-bottom',
'scroll-padding-inline',
'scroll-padding-inline-end',
'scroll-padding-inline-start',
'scroll-padding-left',
'scroll-padding-right',
'scroll-padding-top',
'scroll-snap-coordinate',
'scroll-snap-destination',
'scrollbar-color',
'shape-image-threshold',
'shape-margin',
'shape-outside',
'tab-size',
'text-decoration',
'text-decoration-color',
'text-decoration-thickness',
'text-emphasis',
'text-emphasis-color',
'text-indent',
'text-shadow',
'text-underline-offset',
'top',
'transform',
'transform-origin',
'translate',
'vertical-align',
'visibility',
'width',
'word-spacing',
'z-index',
'zoom',
]);
function optimizeGroupPlayer(players) {
switch (players.length) {
case 0:
return new NoopAnimationPlayer();
case 1:
return players[0];
default:
return new AnimationGroupPlayer(players);
}
}
function normalizeKeyframes$1(normalizer, keyframes, preStyles = new Map(), postStyles = new Map()) {
const errors = [];
const normalizedKeyframes = [];
let previousOffset = -1;
let previousKeyframe = null;
keyframes.forEach((kf) => {
const offset = kf.get('offset');
const isSameOffset = offset == previousOffset;
const normalizedKeyframe = (isSameOffset && previousKeyframe) || new Map();
kf.forEach((val, prop) => {
let normalizedProp = prop;
let normalizedValue = val;
if (prop !== 'offset') {
normalizedProp = normalizer.normalizePropertyName(normalizedProp, errors);
switch (normalizedValue) {
case _PRE_STYLE:
normalizedValue = preStyles.get(prop);
break;
case AUTO_STYLE:
normalizedValue = postStyles.get(prop);
break;
default:
normalizedValue = normalizer.normalizeStyleValue(prop, normalizedProp, normalizedValue, errors);
break;
}
}
normalizedKeyframe.set(normalizedProp, normalizedValue);
});
if (!isSameOffset) {
normalizedKeyframes.push(normalizedKeyframe);
}
previousKeyframe = normalizedKeyframe;
previousOffset = offset;
});
if (errors.length) {
throw animationFailed(errors);
}
return normalizedKeyframes;
}
function listenOnPlayer(player, eventName, event, callback) {
switch (eventName) {
case 'start':
player.onStart(() => callback(event && copyAnimationEvent(event, 'start', player)));
break;
case 'done':
player.onDone(() => callback(event && copyAnimationEvent(event, 'done', player)));
break;
case 'destroy':
player.onDestroy(() => callback(event && copyAnimationEvent(event, 'destroy', player)));
break;
}
}
function copyAnimationEvent(e, phaseName, player) {
const totalTime = player.totalTime;
const disabled = player.disabled ? true : false;
const event = makeAnimationEvent(e.element, e.triggerName, e.fromState, e.toState, phaseName || e.phaseName, totalTime == undefined ? e.totalTime : totalTime, disabled);
const data = e['_data'];
if (data != null) {
event['_data'] = data;
}
return event;
}
function makeAnimationEvent(element, triggerName, fromState, toState, phaseName = '', totalTime = 0, disabled) {
return { element, triggerName, fromState, toState, phaseName, totalTime, disabled: !!disabled };
}
function getOrSetDefaultValue(map, key, defaultValue) {
let value = map.get(key);
if (!value) {
map.set(key, (value = defaultValue));
}
return value;
}
function parseTimelineCommand(command) {
const separatorPos = command.indexOf(':');
const id = command.substring(1, separatorPos);
const action = command.slice(separatorPos + 1);
return [id, action];
}
const documentElement = /* @__PURE__ */ (() => typeof document === 'undefined' ? null : document.documentElement)();
function getParentElement(element) {
const parent = element.parentNode || element.host || null; // consider host to support shadow DOM
if (parent === documentElement) {
return null;
}
return parent;
}
function containsVendorPrefix(prop) {
// Webkit is the only real popular vendor prefix nowadays
// cc: http://shouldiprefix.com/
return prop.substring(1, 6) == 'ebkit'; // webkit or Webkit
}
let _CACHED_BODY = null;
let _IS_WEBKIT = false;
function validateStyleProperty(prop) {
if (!_CACHED_BODY) {
_CACHED_BODY = getBodyNode() || {};
_IS_WEBKIT = _CACHED_BODY.style ? 'WebkitAppearance' in _CACHED_BODY.style : false;
}
let result = true;
if (_CACHED_BODY.style && !containsVendorPrefix(prop)) {
result = prop in _CACHED_BODY.style;
if (!result && _IS_WEBKIT) {
const camelProp = 'Webkit' + prop.charAt(0).toUpperCase() + prop.slice(1);
result = camelProp in _CACHED_BODY.style;
}
}
return result;
}
function validateWebAnimatableStyleProperty(prop) {
return ANIMATABLE_PROP_SET.has(prop);
}
function getBodyNode() {
if (typeof document != 'undefined') {
return document.body;
}
return null;
}
function containsElement(elm1, elm2) {
while (elm2) {
if (elm2 === elm1) {
return true;
}
elm2 = getParentElement(elm2);
}
return false;
}
function invokeQuery(element, selector, multi) {
if (multi) {
return Array.from(element.querySelectorAll(selector));
}
const elem = element.querySelector(selector);
return elem ? [elem] : [];
}
const ONE_SECOND = 1000;
const SUBSTITUTION_EXPR_START = '{{';
const SUBSTITUTION_EXPR_END = '}}';
const ENTER_CLASSNAME = 'ng-enter';
const LEAVE_CLASSNAME = 'ng-leave';
const NG_TRIGGER_CLASSNAME = 'ng-trigger';
const NG_TRIGGER_SELECTOR = '.ng-trigger';
const NG_ANIMATING_CLASSNAME = 'ng-animating';
const NG_ANIMATING_SELECTOR = '.ng-animating';
function resolveTimingValue(value) {
if (typeof value == 'number')
return value;
const matches = value.match(/^(-?[\.\d]+)(m?s)/);
if (!matches || matches.length < 2)
return 0;
return _convertTimeValueToMS(parseFloat(matches[1]), matches[2]);
}
function _convertTimeValueToMS(value, unit) {
switch (unit) {
case 's':
return value * ONE_SECOND;
default: // ms or something else
return value;
}
}
function resolveTiming(timings, errors, allowNegativeValues) {
return timings.hasOwnProperty('duration')
? timings
: parseTimeExpression(timings, errors, allowNegativeValues);
}
const PARSE_TIME_EXPRESSION_REGEX = /^(-?[\.\d]+)(m?s)(?:\s+(-?[\.\d]+)(m?s))?(?:\s+([-a-z]+(?:\(.+?\))?))?$/i;
function parseTimeExpression(exp, errors, allowNegativeValues) {
let duration;
let delay = 0;
let easing = '';
if (typeof exp === 'string') {
const matches = exp.match(PARSE_TIME_EXPRESSION_REGEX);
if (matches === null) {
errors.push(invalidTimingValue(exp));
return { duration: 0, delay: 0, easing: '' };
}
duration = _convertTimeValueToMS(parseFloat(matches[1]), matches[2]);
const delayMatch = matches[3];
if (delayMatch != null) {
delay = _convertTimeValueToMS(parseFloat(delayMatch), matches[4]);
}
const easingVal = matches[5];
if (easingVal) {
easing = easingVal;
}
}
else {
duration = exp;
}
if (!allowNegativeValues) {
let containsErrors = false;
let startIndex = errors.length;
if (duration < 0) {
errors.push(negativeStepValue());
containsErrors = true;
}
if (delay < 0) {
errors.push(negativeDelayValue());
containsErrors = true;
}
if (containsErrors) {
errors.splice(startIndex, 0, invalidTimingValue(exp));
}
}
return { duration, delay, easing };
}
function normalizeKeyframes(keyframes) {
if (!keyframes.length) {
return [];
}
if (keyframes[0] instanceof Map) {
return keyframes;
}
return keyframes.map((kf) => new Map(Object.entries(kf)));
}
function normalizeStyles(styles) {
return Array.isArray(styles) ? new Map(...styles) : new Map(styles);
}
function setStyles(element, styles, formerStyles) {
styles.forEach((val, prop) => {
const camelProp = dashCaseToCamelCase(prop);
if (formerStyles && !formerStyles.has(prop)) {
formerStyles.set(prop, element.style[camelProp]);
}
element.style[camelProp] = val;
});
}
function eraseStyles(element, styles) {
styles.forEach((_, prop) => {
const camelProp = dashCaseToCamelCase(prop);
element.style[camelProp] = '';
});
}
function normalizeAnimationEntry(steps) {
if (Array.isArray(steps)) {
if (steps.length == 1)
return steps[0];
return sequence(steps);
}
return steps;
}
function validateStyleParams(value, options, errors) {
const params = options.params || {};
const matches = extractStyleParams(value);
if (matches.length) {
matches.forEach((varName) => {
if (!params.hasOwnProperty(varName)) {
errors.push(invalidStyleParams(varName));
}
});
}
}
const PARAM_REGEX = /* @__PURE__ */ new RegExp(`${SUBSTITUTION_EXPR_START}\\s*(.+?)\\s*${SUBSTITUTION_EXPR_END}`, 'g');
function extractStyleParams(value) {
let params = [];
if (typeof value === 'string') {
let match;
while ((match = PARAM_REGEX.exec(value))) {
params.push(match[1]);
}
PARAM_REGEX.lastIndex = 0;
}
return params;
}
function interpolateParams(value, params, errors) {
const original = `${value}`;
const str = original.replace(PARAM_REGEX, (_, varName) => {
let localVal = params[varName];
// this means that the value was never overridden by the data passed in by the user
if (localVal == null) {
errors.push(invalidParamValue(varName));
localVal = '';
}
return localVal.toString();
});
// we do this to assert that numeric values stay as they are
return str == original ? value : str;
}
const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
function dashCaseToCamelCase(input) {
return input.replace(DASH_CASE_REGEXP, (...m) => m[1].toUpperCase());
}
function camelCaseToDashCase(input) {
return input.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
}
function allowPreviousPlayerStylesMerge(duration, delay) {
return duration === 0 || delay === 0;
}
function balancePreviousStylesIntoKeyframes(element, keyframes, previousStyles) {
if (previousStyles.size && keyframes.length) {
let startingKeyframe = keyframes[0];
let missingStyleProps = [];
previousStyles.forEach((val, prop) => {
if (!startingKeyframe.has(prop)) {
missingStyleProps.push(prop);
}
startingKeyframe.set(prop, val);
});
if (missingStyleProps.length) {
for (let i = 1; i < keyframes.length; i++) {
let kf = keyframes[i];
missingStyleProps.forEach((prop) => kf.set(prop, computeStyle(element, prop)));
}
}
}
return keyframes;
}
function visitDslNode(visitor, node, context) {
switch (node.type) {
case AnimationMetadataType.Trigger:
return visitor.visitTrigger(node, context);
case AnimationMetadataType.State:
return visitor.visitState(node, context);
case AnimationMetadataType.Transition:
return visitor.visitTransition(node, context);
case AnimationMetadataType.Sequence:
return visitor.visitSequence(node, context);
case AnimationMetadataType.Group:
return visitor.visitGroup(node, context);
case AnimationMetadataType.Animate:
return visitor.visitAnimate(node, context);
case AnimationMetadataType.Keyframes:
return visitor.visitKeyframes(node, context);
case AnimationMetadataType.Style:
return visitor.visitStyle(node, context);
case AnimationMetadataType.Reference:
return visitor.visitReference(node, context);
case AnimationMetadataType.AnimateChild:
return visitor.visitAnimateChild(node, context);
case AnimationMetadataType.AnimateRef:
return visitor.visitAnimateRef(node, context);
case AnimationMetadataType.Query:
return visitor.visitQuery(node, context);
case AnimationMetadataType.Stagger:
return visitor.visitStagger(node, context);
default:
throw invalidNodeType(node.type);
}
}
function computeStyle(element, prop) {
return window.getComputedStyle(element)[prop];
}
export { ENTER_CLASSNAME, LEAVE_CLASSNAME, NG_ANIMATING_CLASSNAME, NG_ANIMATING_SELECTOR, NG_TRIGGER_CLASSNAME, NG_TRIGGER_SELECTOR, SUBSTITUTION_EXPR_START, allowPreviousPlayerStylesMerge, balancePreviousStylesIntoKeyframes, buildingFailed, camelCaseToDashCase, computeStyle, containsElement, createAnimationFailed, dashCaseToCamelCase, eraseStyles, extractStyleParams, getOrSetDefaultValue, getParentElement, interpolateParams, invalidCssUnitValue, invalidDefinition, invalidExpression, invalidKeyframes, invalidOffset, invalidParallelAnimation, invalidQuery, invalidStagger, invalidState, invalidStyleValue, invalidTransitionAlias, invalidTrigger, invokeQuery, keyframeOffsetsOutOfOrder, keyframesMissingOffsets, listenOnPlayer, makeAnimationEvent, missingEvent, missingOrDestroyedAnimation, missingPlayer, missingTrigger, normalizeAnimationEntry, normalizeKeyframes$1 as normalizeKeyframes, normalizeKeyframes as normalizeKeyframes$1, normalizeStyles, optimizeGroupPlayer, parseTimelineCommand, registerFailed, resolveTiming, resolveTimingValue, setStyles, transitionFailed, triggerBuildFailed, triggerTransitionsFailed, unregisteredTrigger, unsupportedTriggerEvent, validateStyleParams, validateStyleProperty, validateWebAnimatableStyleProperty, validationFailed, visitDslNode };
//# sourceMappingURL=util.mjs.map

1
node_modules/@angular/animations/fesm2022/util.mjs.map generated vendored Executable file

File diff suppressed because one or more lines are too long

252
node_modules/@angular/animations/index.d.ts generated vendored Executable file
View File

@@ -0,0 +1,252 @@
/**
* @license Angular v20.3.11
* (c) 2010-2025 Google LLC. https://angular.dev/
* License: MIT
*/
import * as i0 from '@angular/core';
import { RendererFactory2 } from '@angular/core';
import { AnimationMetadata, AnimationOptions, AnimationPlayer } from './animation_player.d.js';
export { AUTO_STYLE, AnimateChildOptions, AnimateTimings, AnimationAnimateChildMetadata, AnimationAnimateMetadata, AnimationAnimateRefMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadataType, AnimationQueryMetadata, AnimationQueryOptions, AnimationReferenceMetadata, AnimationSequenceMetadata, AnimationStaggerMetadata, AnimationStateMetadata, AnimationStyleMetadata, AnimationTransitionMetadata, AnimationTriggerMetadata, NoopAnimationPlayer, animate, animateChild, animation, group, keyframes, query, sequence, stagger, state, style, transition, trigger, useAnimation, ɵStyleData, ɵStyleDataMap } from './animation_player.d.js';
/**
* An injectable service that produces an animation sequence programmatically within an
* Angular component or directive.
* Provided by the `BrowserAnimationsModule` or `NoopAnimationsModule`.
*
* @usageNotes
*
* To use this service, add it to your component or directive as a dependency.
* The service is instantiated along with your component.
*
* Apps do not typically need to create their own animation players, but if you
* do need to, follow these steps:
*
* 1. Use the <code>[AnimationBuilder.build](api/animations/AnimationBuilder#build)()</code> method
* to create a programmatic animation. The method returns an `AnimationFactory` instance.
*
* 2. Use the factory object to create an `AnimationPlayer` and attach it to a DOM element.
*
* 3. Use the player object to control the animation programmatically.
*
* For example:
*
* ```ts
* // import the service from BrowserAnimationsModule
* import {AnimationBuilder} from '@angular/animations';
* // require the service as a dependency
* class MyCmp {
* constructor(private _builder: AnimationBuilder) {}
*
* makeAnimation(element: any) {
* // first define a reusable animation
* const myAnimation = this._builder.build([
* style({ width: 0 }),
* animate(1000, style({ width: '100px' }))
* ]);
*
* // use the returned factory object to create a player
* const player = myAnimation.create(element);
*
* player.play();
* }
* }
* ```
*
* @publicApi
*
* @deprecated 20.2 Use `animate.enter` or `animate.leave` instead. Intent to remove in v23
*/
declare abstract class AnimationBuilder {
/**
* Builds a factory for producing a defined animation.
* @param animation A reusable animation definition.
* @returns A factory object that can create a player for the defined animation.
* @see {@link animate}
*/
abstract build(animation: AnimationMetadata | AnimationMetadata[]): AnimationFactory;
static ɵfac: i0.ɵɵFactoryDeclaration<AnimationBuilder, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<AnimationBuilder>;
}
/**
* A factory object returned from the
* <code>[AnimationBuilder.build](api/animations/AnimationBuilder#build)()</code>
* method.
*
* @publicApi
*
* @deprecated 20.2 Use `animate.enter` or `animate.leave` instead. Intent to remove in v23
*/
declare abstract class AnimationFactory {
/**
* Creates an `AnimationPlayer` instance for the reusable animation defined by
* the <code>[AnimationBuilder.build](api/animations/AnimationBuilder#build)()</code>
* method that created this factory and attaches the new player a DOM element.
*
* @param element The DOM element to which to attach the player.
* @param options A set of options that can include a time delay and
* additional developer-defined parameters.
*/
abstract create(element: any, options?: AnimationOptions): AnimationPlayer;
}
declare class BrowserAnimationBuilder extends AnimationBuilder {
private animationModuleType;
private _nextAnimationId;
private _renderer;
constructor(rootRenderer: RendererFactory2, doc: Document);
build(animation: AnimationMetadata | AnimationMetadata[]): AnimationFactory;
static ɵfac: i0.ɵɵFactoryDeclaration<BrowserAnimationBuilder, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<BrowserAnimationBuilder>;
}
/**
* An instance of this class is returned as an event parameter when an animation
* callback is captured for an animation either during the start or done phase.
*
* ```ts
* @Component({
* host: {
* '[@myAnimationTrigger]': 'someExpression',
* '(@myAnimationTrigger.start)': 'captureStartEvent($event)',
* '(@myAnimationTrigger.done)': 'captureDoneEvent($event)',
* },
* animations: [
* trigger("myAnimationTrigger", [
* // ...
* ])
* ]
* })
* class MyComponent {
* someExpression: any = false;
* captureStartEvent(event: AnimationEvent) {
* // the toState, fromState and totalTime data is accessible from the event variable
* }
*
* captureDoneEvent(event: AnimationEvent) {
* // the toState, fromState and totalTime data is accessible from the event variable
* }
* }
* ```
*
* @publicApi
*
* @deprecated 20.2 Use `animate.enter` or `animate.leave` instead. Intent to remove in v23
*/
interface AnimationEvent {
/**
* The name of the state from which the animation is triggered.
*/
fromState: string;
/**
* The name of the state in which the animation completes.
*/
toState: string;
/**
* The time it takes the animation to complete, in milliseconds.
*/
totalTime: number;
/**
* The animation phase in which the callback was invoked, one of
* "start" or "done".
*/
phaseName: string;
/**
* The element to which the animation is attached.
*/
element: any;
/**
* Internal.
*/
triggerName: string;
/**
* Internal.
*/
disabled: boolean;
}
/**
* The list of error codes used in runtime code of the `animations` package.
* Reserved error code range: 3000-3999.
*/
declare const enum RuntimeErrorCode {
INVALID_TIMING_VALUE = 3000,
INVALID_STYLE_PARAMS = 3001,
INVALID_STYLE_VALUE = 3002,
INVALID_PARAM_VALUE = 3003,
INVALID_NODE_TYPE = 3004,
INVALID_CSS_UNIT_VALUE = 3005,
INVALID_TRIGGER = 3006,
INVALID_DEFINITION = 3007,
INVALID_STATE = 3008,
INVALID_PROPERTY = 3009,
INVALID_PARALLEL_ANIMATION = 3010,
INVALID_KEYFRAMES = 3011,
INVALID_OFFSET = 3012,
INVALID_STAGGER = 3013,
INVALID_QUERY = 3014,
INVALID_EXPRESSION = 3015,
INVALID_TRANSITION_ALIAS = 3016,
NEGATIVE_STEP_VALUE = 3100,
NEGATIVE_DELAY_VALUE = 3101,
KEYFRAME_OFFSETS_OUT_OF_ORDER = 3200,
KEYFRAMES_MISSING_OFFSETS = 3202,
MISSING_OR_DESTROYED_ANIMATION = 3300,
MISSING_PLAYER = 3301,
MISSING_TRIGGER = 3302,
MISSING_EVENT = 3303,
UNSUPPORTED_TRIGGER_EVENT = 3400,
UNREGISTERED_TRIGGER = 3401,
TRIGGER_TRANSITIONS_FAILED = 3402,
TRIGGER_PARSING_FAILED = 3403,
TRIGGER_BUILD_FAILED = 3404,
VALIDATION_FAILED = 3500,
BUILDING_FAILED = 3501,
ANIMATION_FAILED = 3502,
REGISTRATION_FAILED = 3503,
CREATE_ANIMATION_FAILED = 3504,
TRANSITION_FAILED = 3505,
BROWSER_ANIMATION_BUILDER_INJECTED_WITHOUT_ANIMATIONS = 3600
}
/**
* A programmatic controller for a group of reusable animations.
* Used internally to control animations.
*
* @see {@link AnimationPlayer}
* @see {@link animations/group group}
*
*/
declare class AnimationGroupPlayer implements AnimationPlayer {
private _onDoneFns;
private _onStartFns;
private _finished;
private _started;
private _destroyed;
private _onDestroyFns;
parentPlayer: AnimationPlayer | null;
totalTime: number;
readonly players: AnimationPlayer[];
constructor(_players: AnimationPlayer[]);
private _onFinish;
init(): void;
onStart(fn: () => void): void;
private _onStart;
onDone(fn: () => void): void;
onDestroy(fn: () => void): void;
hasStarted(): boolean;
play(): void;
pause(): void;
restart(): void;
finish(): void;
destroy(): void;
private _onDestroy;
reset(): void;
setPosition(p: number): void;
getPosition(): number;
beforeDestroy(): void;
}
declare const ɵPRE_STYLE = "!";
export { AnimationBuilder, AnimationFactory, AnimationMetadata, AnimationOptions, AnimationPlayer, AnimationGroupPlayer as ɵAnimationGroupPlayer, BrowserAnimationBuilder as ɵBrowserAnimationBuilder, ɵPRE_STYLE, RuntimeErrorCode as ɵRuntimeErrorCode };
export type { AnimationEvent };

62
node_modules/@angular/animations/package.json generated vendored Executable file
View File

@@ -0,0 +1,62 @@
{
"name": "@angular/animations",
"version": "20.3.11",
"description": "Angular - animations integration with web-animations",
"author": "angular",
"license": "MIT",
"engines": {
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
},
"dependencies": {
"tslib": "^2.3.0"
},
"peerDependencies": {
"@angular/core": "20.3.11"
},
"repository": {
"type": "git",
"url": "https://github.com/angular/angular.git",
"directory": "packages/animations"
},
"ng-update": {
"packageGroup": [
"@angular/core",
"@angular/bazel",
"@angular/common",
"@angular/compiler",
"@angular/compiler-cli",
"@angular/animations",
"@angular/elements",
"@angular/platform-browser",
"@angular/platform-browser-dynamic",
"@angular/forms",
"@angular/platform-server",
"@angular/upgrade",
"@angular/router",
"@angular/language-service",
"@angular/localize",
"@angular/service-worker"
]
},
"sideEffects": false,
"module": "./fesm2022/animations.mjs",
"typings": "./index.d.ts",
"type": "module",
"exports": {
"./package.json": {
"default": "./package.json"
},
".": {
"types": "./index.d.ts",
"default": "./fesm2022/animations.mjs"
},
"./browser": {
"types": "./browser/index.d.ts",
"default": "./fesm2022/browser.mjs"
},
"./browser/testing": {
"types": "./browser/testing/index.d.ts",
"default": "./fesm2022/browser/testing.mjs"
}
}
}

7
node_modules/@angular/build/.browserslistrc generated vendored Executable file
View File

@@ -0,0 +1,7 @@
Chrome >= 107
ChromeAndroid >= 107
Edge >= 107
Firefox >= 104
FirefoxAndroid >= 104
Safari >= 16
iOS >= 16

21
node_modules/@angular/build/LICENSE generated vendored Executable file
View File

@@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2010-2025 Google LLC. https://angular.dev/license
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

5
node_modules/@angular/build/README.md generated vendored Executable file
View File

@@ -0,0 +1,5 @@
# Angular Build System for Applications and Libraries
The sources for this package are in the [Angular CLI](https://github.com/angular/angular-cli) repository. Please file issues and pull requests against that repository.
Usage information and reference details can be found in repository [README](https://github.com/angular/angular-cli/blob/main/README.md) file.

34
node_modules/@angular/build/builders.json generated vendored Executable file
View File

@@ -0,0 +1,34 @@
{
"builders": {
"application": {
"implementation": "./src/builders/application/index",
"schema": "./src/builders/application/schema.json",
"description": "Build an application."
},
"dev-server": {
"implementation": "./src/builders/dev-server/index",
"schema": "./src/builders/dev-server/schema.json",
"description": "Execute a development server for an application."
},
"extract-i18n": {
"implementation": "./src/builders/extract-i18n/index",
"schema": "./src/builders/extract-i18n/schema.json",
"description": "Extract i18n messages from an application."
},
"karma": {
"implementation": "./src/builders/karma",
"schema": "./src/builders/karma/schema.json",
"description": "Run Karma unit tests."
},
"ng-packagr": {
"implementation": "./src/builders/ng-packagr/index",
"schema": "./src/builders/ng-packagr/schema.json",
"description": "Build a library with ng-packagr."
},
"unit-test": {
"implementation": "./src/builders/unit-test",
"schema": "./src/builders/unit-test/schema.json",
"description": "[EXPERIMENTAL] Run application unit tests."
}
}
}

8
node_modules/@angular/build/index.d.ts generated vendored Executable file
View File

@@ -0,0 +1,8 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
export * from './src/index';

24
node_modules/@angular/build/index.js generated vendored Executable file
View File

@@ -0,0 +1,24 @@
"use strict";
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./src/index"), exports);

1
node_modules/@angular/build/node_modules/.bin/vite generated vendored Symbolic link
View File

@@ -0,0 +1 @@
../vite/bin/vite.js

View File

@@ -0,0 +1,22 @@
Copyright (c) 2025 Simon Boudrias
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,102 @@
# `@inquirer/confirm`
Simple interactive command line prompt to gather boolean input from users.
![Confirm prompt](https://cdn.rawgit.com/SBoudrias/Inquirer.js/28ae8337ba51d93e359ef4f7ee24e79b69898962/assets/screenshots/confirm.svg)
# Special Thanks
<div align="center" markdown="1">
[![Graphite](https://github.com/user-attachments/assets/53db40ca-2254-481a-a094-6597f8716e29)](https://graphite.dev/?utm_source=npmjs&utm_medium=repo&utm_campaign=inquirerjs)<br>
### [Graphite is the AI developer productivity platform helping teams on GitHub ship higher quality software, faster](https://graphite.dev/?utm_source=npmjs&utm_medium=repo&utm_campaign=inquirerjs)
</div>
# Installation
<table>
<tr>
<th>npm</th>
<th>yarn</th>
</tr>
<tr>
<td>
```sh
npm install @inquirer/prompts
```
</td>
<td>
```sh
yarn add @inquirer/prompts
```
</td>
</tr>
<tr>
<td colSpan="2" align="center">Or</td>
</tr>
<tr>
<td>
```sh
npm install @inquirer/confirm
```
</td>
<td>
```sh
yarn add @inquirer/confirm
```
</td>
</tr>
</table>
# Usage
```js
import { confirm } from '@inquirer/prompts';
// Or
// import confirm from '@inquirer/confirm';
const answer = await confirm({ message: 'Continue?' });
```
## Options
| Property | Type | Required | Description |
| ----------- | ----------------------- | -------- | ------------------------------------------------------- |
| message | `string` | yes | The question to ask |
| default | `boolean` | no | Default answer (true or false) |
| transformer | `(boolean) => string` | no | Transform the prompt printed message to a custom string |
| theme | [See Theming](#Theming) | no | Customize look of the prompt. |
## Theming
You can theme a prompt by passing a `theme` object option. The theme object only need to includes the keys you wish to modify, we'll fallback on the defaults for the rest.
```ts
type Theme = {
prefix: string | { idle: string; done: string };
spinner: {
interval: number;
frames: string[];
};
style: {
answer: (text: string) => string;
message: (text: string, status: 'idle' | 'done' | 'loading') => string;
defaultAnswer: (text: string) => string;
};
};
```
# License
Copyright (c) 2023 Simon Boudrias (twitter: [@vaxilart](https://twitter.com/Vaxilart))<br/>
Licensed under the MIT license.

View File

@@ -0,0 +1,10 @@
import { type Theme } from '@inquirer/core';
import type { PartialDeep } from '@inquirer/type';
type ConfirmConfig = {
message: string;
default?: boolean;
transformer?: (value: boolean) => string;
theme?: PartialDeep<Theme>;
};
declare const _default: import("@inquirer/type").Prompt<boolean, ConfirmConfig>;
export default _default;

View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("@inquirer/core");
function getBooleanValue(value, defaultValue) {
let answer = defaultValue !== false;
if (/^(y|yes)/i.test(value))
answer = true;
else if (/^(n|no)/i.test(value))
answer = false;
return answer;
}
function boolToString(value) {
return value ? 'Yes' : 'No';
}
exports.default = (0, core_1.createPrompt)((config, done) => {
const { transformer = boolToString } = config;
const [status, setStatus] = (0, core_1.useState)('idle');
const [value, setValue] = (0, core_1.useState)('');
const theme = (0, core_1.makeTheme)(config.theme);
const prefix = (0, core_1.usePrefix)({ status, theme });
(0, core_1.useKeypress)((key, rl) => {
if ((0, core_1.isEnterKey)(key)) {
const answer = getBooleanValue(value, config.default);
setValue(transformer(answer));
setStatus('done');
done(answer);
}
else if (key.name === 'tab') {
const answer = boolToString(!getBooleanValue(value, config.default));
rl.clearLine(0); // Remove the tab character.
rl.write(answer);
setValue(answer);
}
else {
setValue(rl.line);
}
});
let formattedValue = value;
let defaultValue = '';
if (status === 'done') {
formattedValue = theme.style.answer(value);
}
else {
defaultValue = ` ${theme.style.defaultAnswer(config.default === false ? 'y/N' : 'Y/n')}`;
}
const message = theme.style.message(config.message, status);
return `${prefix} ${message}${defaultValue} ${formattedValue}`;
});

View File

@@ -0,0 +1,3 @@
{
"type": "commonjs"
}

View File

@@ -0,0 +1,10 @@
import { type Theme } from '@inquirer/core';
import type { PartialDeep } from '@inquirer/type';
type ConfirmConfig = {
message: string;
default?: boolean;
transformer?: (value: boolean) => string;
theme?: PartialDeep<Theme>;
};
declare const _default: import("@inquirer/type").Prompt<boolean, ConfirmConfig>;
export default _default;

View File

@@ -0,0 +1,46 @@
import { createPrompt, useState, useKeypress, isEnterKey, usePrefix, makeTheme, } from '@inquirer/core';
function getBooleanValue(value, defaultValue) {
let answer = defaultValue !== false;
if (/^(y|yes)/i.test(value))
answer = true;
else if (/^(n|no)/i.test(value))
answer = false;
return answer;
}
function boolToString(value) {
return value ? 'Yes' : 'No';
}
export default createPrompt((config, done) => {
const { transformer = boolToString } = config;
const [status, setStatus] = useState('idle');
const [value, setValue] = useState('');
const theme = makeTheme(config.theme);
const prefix = usePrefix({ status, theme });
useKeypress((key, rl) => {
if (isEnterKey(key)) {
const answer = getBooleanValue(value, config.default);
setValue(transformer(answer));
setStatus('done');
done(answer);
}
else if (key.name === 'tab') {
const answer = boolToString(!getBooleanValue(value, config.default));
rl.clearLine(0); // Remove the tab character.
rl.write(answer);
setValue(answer);
}
else {
setValue(rl.line);
}
});
let formattedValue = value;
let defaultValue = '';
if (status === 'done') {
formattedValue = theme.style.answer(value);
}
else {
defaultValue = ` ${theme.style.defaultAnswer(config.default === false ? 'y/N' : 'Y/n')}`;
}
const message = theme.style.message(config.message, status);
return `${prefix} ${message}${defaultValue} ${formattedValue}`;
});

View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

View File

@@ -0,0 +1,109 @@
{
"name": "@inquirer/confirm",
"version": "5.1.14",
"description": "Inquirer confirm prompt",
"keywords": [
"answer",
"answers",
"ask",
"base",
"cli",
"command",
"command-line",
"confirm",
"enquirer",
"generate",
"generator",
"hyper",
"input",
"inquire",
"inquirer",
"interface",
"iterm",
"javascript",
"menu",
"node",
"nodejs",
"prompt",
"promptly",
"prompts",
"question",
"readline",
"scaffold",
"scaffolder",
"scaffolding",
"stdin",
"stdout",
"terminal",
"tty",
"ui",
"yeoman",
"yo",
"zsh"
],
"homepage": "https://github.com/SBoudrias/Inquirer.js/blob/main/packages/confirm/README.md",
"repository": {
"type": "git",
"url": "https://github.com/SBoudrias/Inquirer.js.git"
},
"license": "MIT",
"author": "Simon Boudrias <admin@simonboudrias.com>",
"sideEffects": false,
"type": "module",
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.js"
}
}
},
"main": "./dist/commonjs/index.js",
"module": "./dist/esm/index.js",
"types": "./dist/commonjs/index.d.ts",
"files": [
"dist"
],
"scripts": {
"attw": "attw --pack",
"tsc": "tshy"
},
"dependencies": {
"@inquirer/core": "^10.1.15",
"@inquirer/type": "^3.0.8"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.18.2",
"@inquirer/testing": "^2.1.49",
"tshy": "^3.0.2"
},
"engines": {
"node": ">=18"
},
"publishConfig": {
"access": "public"
},
"tshy": {
"exclude": [
"src/**/*.test.ts"
],
"exports": {
"./package.json": "./package.json",
".": "./src/index.ts"
}
},
"peerDependencies": {
"@types/node": ">=18"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
}
},
"gitHead": "c1a755fe8b50377b685ea5951e0794985ce8d356"
}

View File

@@ -0,0 +1,22 @@
Copyright (c) 2025 Simon Boudrias
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,383 @@
# `@inquirer/core`
The `@inquirer/core` package is the library enabling the creation of Inquirer prompts.
It aims to implements a lightweight API similar to React hooks - but without JSX.
# Installation
<table>
<tr>
<th>npm</th>
<th>yarn</th>
</tr>
<tr>
<td>
```sh
npm install @inquirer/core
```
</td>
<td>
```sh
yarn add @inquirer/core
```
</td>
</tr>
</table>
# Usage
## Basic concept
Visual terminal apps are at their core strings rendered onto the terminal.
The most basic prompt is a function returning a string that'll be rendered in the terminal. This function will run every time the prompt state change, and the new returned string will replace the previously rendered one. The prompt cursor appears after the string.
Wrapping the rendering function with `createPrompt()` will setup the rendering layer, inject the state management utilities, and wait until the `done` callback is called.
```ts
import { createPrompt } from '@inquirer/core';
const input = createPrompt((config, done) => {
// Implement logic
return '? My question';
});
// And it is then called as
const answer = await input({
/* config */
});
```
## Hooks
State management and user interactions are handled through hooks. Hooks are common [within the React ecosystem](https://react.dev/reference/react/hooks), and Inquirer reimplement the common ones.
### State hook
State lets a component “remember” information like user input. For example, an input prompt can use state to store the input value, while a list prompt can use state to track the cursor index.
`useState` declares a state variable that you can update directly.
```ts
import { createPrompt, useState } from '@inquirer/core';
const input = createPrompt((config, done) => {
const [index, setIndex] = useState(0);
// ...
```
### Keypress hook
Almost all prompts need to react to user actions. In a terminal, this is done through typing.
`useKeypress` allows you to react to keypress events, and access the prompt line.
```ts
const input = createPrompt((config, done) => {
useKeypress((key) => {
if (key.name === 'enter') {
done(answer);
}
});
// ...
```
Behind the scenes, Inquirer prompts are wrappers around [readlines](https://nodejs.org/api/readline.html). Aside the keypress event object, the hook also pass the active readline instance to the event handler.
```ts
const input = createPrompt((config, done) => {
useKeypress((key, readline) => {
setValue(readline.line);
});
// ...
```
### Ref hook
Refs let a prompt hold some information that isnt used for rendering, like a class instance or a timeout ID. Unlike with state, updating a ref does not re-render your prompt. Refs are an “escape hatch” from the rendering paradigm.
`useRef` declares a ref. You can hold any value in it, but most often its used to hold a timeout ID.
```ts
const input = createPrompt((config, done) => {
const timeout = useRef(null);
// ...
```
### Effect Hook
Effects let a prompt connect to and synchronize with external systems. This includes dealing with network or animations.
`useEffect` connects a component to an external system.
```ts
const chat = createPrompt((config, done) => {
useEffect(() => {
const connection = createConnection(roomId);
connection.connect();
return () => connection.disconnect();
}, [roomId]);
// ...
```
### Performance hook
A common way to optimize re-rendering performance is to skip unnecessary work. For example, you can tell Inquirer to reuse a cached calculation or to skip a re-render if the data has not changed since the previous render.
`useMemo` lets you cache the result of an expensive calculation.
```ts
const todoSelect = createPrompt((config, done) => {
const visibleTodos = useMemo(() => filterTodos(todos, tab), [todos, tab]);
// ...
```
### Rendering hooks
#### Prefix / loading
All default prompts, and most custom ones, uses a prefix at the beginning of the prompt line. This helps visually delineate different questions, and provides a convenient area to render a loading spinner.
`usePrefix` is a built-in hook to do this.
```ts
const input = createPrompt((config, done) => {
const prefix = usePrefix({ status });
return `${prefix} My question`;
});
```
#### Pagination
When looping through a long list of options (like in the `select` prompt), paginating the results appearing on the screen at once can be necessary. The `usePagination` hook is the utility used within the `select` and `checkbox` prompts to cycle through the list of options.
Pagination works by taking in the list of options and returning a subset of the rendered items that fit within the page. The hook takes in a few options. It needs a list of options (`items`), and a `pageSize` which is the number of lines to be rendered. The `active` index is the index of the currently selected/selectable item. The `loop` option is a boolean that indicates if the list should loop around when reaching the end: this is the default behavior. The pagination hook renders items only as necessary, so it takes a function that can render an item at an index, including an `active` state, called `renderItem`.
```js
export default createPrompt((config, done) => {
const [active, setActive] = useState(0);
const allChoices = config.choices.map((choice) => choice.name);
const page = usePagination({
items: allChoices,
active: active,
renderItem: ({ item, index, isActive }) => `${isActive ? ">" : " "}${index}. ${item.toString()}`
pageSize: config.pageSize,
loop: config.loop,
});
return `... ${page}`;
});
```
## `createPrompt()` API
As we saw earlier, the rendering function should return a string, and eventually call `done` to close the prompt and return the answer.
```ts
const input = createPrompt((config, done) => {
const [value, setValue] = useState();
useKeypress((key, readline) => {
if (key.name === 'enter') {
done(answer);
} else {
setValue(readline.line);
}
});
return `? ${config.message} ${value}`;
});
```
The rendering function can also return a tuple of 2 string (`[string, string]`.) The first string represents the prompt. The second one is content to render under the prompt, like an error message. The text input cursor will appear after the first string.
```ts
const number = createPrompt((config, done) => {
// Add some logic here
return [`? My question ${input}`, `! The input must be a number`];
});
```
### Typescript
If using typescript, `createPrompt` takes 2 generic arguments.
```ts
// createPrompt<Value, Config>
const input = createPrompt<string, { message: string }>(// ...
```
The first one is the type of the resolved value
```ts
const answer: string = await input();
```
The second one is the type of the prompt config; in other words the interface the created prompt will provide to users.
```ts
const answer = await input({
message: 'My question',
});
```
## Key utilities
Listening for keypress events inside an inquirer prompt is a very common pattern. To ease this, we export a few utility functions taking in the keypress event object and return a boolean:
- `isEnterKey()`
- `isBackspaceKey()`
- `isSpaceKey()`
- `isUpKey()` - Note: this utility will handle vim and emacs keybindings (up, `k`, and `ctrl+p`)
- `isDownKey()` - Note: this utility will handle vim and emacs keybindings (down, `j`, and `ctrl+n`)
- `isNumberKey()` one of 1, 2, 3, 4, 5, 6, 7, 8, 9, 0
## Theming
Theming utilities will allow you to expose customization of the prompt style. Inquirer also has a few standard theme values shared across all the official prompts.
To allow standard customization:
```ts
import { createPrompt, usePrefix, makeTheme, type Theme } from '@inquirer/core';
import type { PartialDeep } from '@inquirer/type';
type PromptConfig = {
theme?: PartialDeep<Theme>;
};
export default createPrompt<string, PromptConfig>((config, done) => {
const theme = makeTheme(config.theme);
const prefix = usePrefix({ status, theme });
return `${prefix} ${theme.style.highlight('hello')}`;
});
```
To setup a custom theme:
```ts
import { createPrompt, makeTheme, type Theme } from '@inquirer/core';
import type { PartialDeep } from '@inquirer/type';
type PromptTheme = {};
const promptTheme: PromptTheme = {
icon: '!',
};
type PromptConfig = {
theme?: PartialDeep<Theme<PromptTheme>>;
};
export default createPrompt<string, PromptConfig>((config, done) => {
const theme = makeTheme(promptTheme, config.theme);
const prefix = usePrefix({ status, theme });
return `${prefix} ${theme.icon}`;
});
```
The [default theme keys cover](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/core/src/lib/theme.ts):
```ts
type DefaultTheme = {
prefix: string | { idle: string; done: string };
spinner: {
interval: number;
frames: string[];
};
style: {
answer: (text: string) => string;
message: (text: string, status: 'idle' | 'done' | 'loading') => string;
error: (text: string) => string;
defaultAnswer: (text: string) => string;
help: (text: string) => string;
highlight: (text: string) => string;
key: (text: string) => string;
};
};
```
# Examples
You can refer to any `@inquirer/prompts` prompts for real examples:
- [Confirm Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/confirm/src/index.ts)
- [Input Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/input/src/index.ts)
- [Password Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/password/src/index.ts)
- [Editor Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/editor/src/index.ts)
- [Select Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/select/src/index.ts)
- [Checkbox Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/checkbox/src/index.ts)
- [Rawlist Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/rawlist/src/index.ts)
- [Expand Prompt](https://github.com/SBoudrias/Inquirer.js/blob/main/packages/expand/src/index.ts)
```ts
import colors from 'yoctocolors';
import {
createPrompt,
useState,
useKeypress,
isEnterKey,
usePrefix,
type Status,
} from '@inquirer/core';
const confirm = createPrompt<boolean, { message: string; default?: boolean }>(
(config, done) => {
const [status, setStatus] = useState<Status>('idle');
const [value, setValue] = useState('');
const prefix = usePrefix({});
useKeypress((key, rl) => {
if (isEnterKey(key)) {
const answer = value ? /^y(es)?/i.test(value) : config.default !== false;
setValue(answer ? 'yes' : 'no');
setStatus('done');
done(answer);
} else {
setValue(rl.line);
}
});
let formattedValue = value;
let defaultValue = '';
if (status === 'done') {
formattedValue = colors.cyan(value);
} else {
defaultValue = colors.dim(config.default === false ? ' (y/N)' : ' (Y/n)');
}
const message = colors.bold(config.message);
return `${prefix} ${message}${defaultValue} ${formattedValue}`;
},
);
/**
* Which then can be used like this:
*/
const answer = await confirm({ message: 'Do you want to continue?' });
```
# License
Copyright (c) 2023 Simon Boudrias (twitter: [@vaxilart](https://twitter.com/Vaxilart))<br/>
Licensed under the MIT license.

View File

@@ -0,0 +1,13 @@
export { isUpKey, isDownKey, isSpaceKey, isBackspaceKey, isTabKey, isNumberKey, isEnterKey, type KeypressEvent, type Keybinding, } from './lib/key.ts';
export * from './lib/errors.ts';
export { usePrefix } from './lib/use-prefix.ts';
export { useState } from './lib/use-state.ts';
export { useEffect } from './lib/use-effect.ts';
export { useMemo } from './lib/use-memo.ts';
export { useRef } from './lib/use-ref.ts';
export { useKeypress } from './lib/use-keypress.ts';
export { makeTheme } from './lib/make-theme.ts';
export type { Theme, Status } from './lib/theme.ts';
export { usePagination } from './lib/pagination/use-pagination.ts';
export { createPrompt } from './lib/create-prompt.ts';
export { Separator } from './lib/Separator.ts';

View File

@@ -0,0 +1,46 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Separator = exports.createPrompt = exports.usePagination = exports.makeTheme = exports.useKeypress = exports.useRef = exports.useMemo = exports.useEffect = exports.useState = exports.usePrefix = exports.isEnterKey = exports.isNumberKey = exports.isTabKey = exports.isBackspaceKey = exports.isSpaceKey = exports.isDownKey = exports.isUpKey = void 0;
var key_ts_1 = require("./lib/key.js");
Object.defineProperty(exports, "isUpKey", { enumerable: true, get: function () { return key_ts_1.isUpKey; } });
Object.defineProperty(exports, "isDownKey", { enumerable: true, get: function () { return key_ts_1.isDownKey; } });
Object.defineProperty(exports, "isSpaceKey", { enumerable: true, get: function () { return key_ts_1.isSpaceKey; } });
Object.defineProperty(exports, "isBackspaceKey", { enumerable: true, get: function () { return key_ts_1.isBackspaceKey; } });
Object.defineProperty(exports, "isTabKey", { enumerable: true, get: function () { return key_ts_1.isTabKey; } });
Object.defineProperty(exports, "isNumberKey", { enumerable: true, get: function () { return key_ts_1.isNumberKey; } });
Object.defineProperty(exports, "isEnterKey", { enumerable: true, get: function () { return key_ts_1.isEnterKey; } });
__exportStar(require("./lib/errors.js"), exports);
var use_prefix_ts_1 = require("./lib/use-prefix.js");
Object.defineProperty(exports, "usePrefix", { enumerable: true, get: function () { return use_prefix_ts_1.usePrefix; } });
var use_state_ts_1 = require("./lib/use-state.js");
Object.defineProperty(exports, "useState", { enumerable: true, get: function () { return use_state_ts_1.useState; } });
var use_effect_ts_1 = require("./lib/use-effect.js");
Object.defineProperty(exports, "useEffect", { enumerable: true, get: function () { return use_effect_ts_1.useEffect; } });
var use_memo_ts_1 = require("./lib/use-memo.js");
Object.defineProperty(exports, "useMemo", { enumerable: true, get: function () { return use_memo_ts_1.useMemo; } });
var use_ref_ts_1 = require("./lib/use-ref.js");
Object.defineProperty(exports, "useRef", { enumerable: true, get: function () { return use_ref_ts_1.useRef; } });
var use_keypress_ts_1 = require("./lib/use-keypress.js");
Object.defineProperty(exports, "useKeypress", { enumerable: true, get: function () { return use_keypress_ts_1.useKeypress; } });
var make_theme_ts_1 = require("./lib/make-theme.js");
Object.defineProperty(exports, "makeTheme", { enumerable: true, get: function () { return make_theme_ts_1.makeTheme; } });
var use_pagination_ts_1 = require("./lib/pagination/use-pagination.js");
Object.defineProperty(exports, "usePagination", { enumerable: true, get: function () { return use_pagination_ts_1.usePagination; } });
var create_prompt_ts_1 = require("./lib/create-prompt.js");
Object.defineProperty(exports, "createPrompt", { enumerable: true, get: function () { return create_prompt_ts_1.createPrompt; } });
var Separator_ts_1 = require("./lib/Separator.js");
Object.defineProperty(exports, "Separator", { enumerable: true, get: function () { return Separator_ts_1.Separator; } });

View File

@@ -0,0 +1,10 @@
/**
* Separator object
* Used to space/separate choices group
*/
export declare class Separator {
readonly separator: string;
readonly type: string;
constructor(separator?: string);
static isSeparator(choice: unknown): choice is Separator;
}

View File

@@ -0,0 +1,28 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Separator = void 0;
const yoctocolors_cjs_1 = __importDefault(require("yoctocolors-cjs"));
const figures_1 = __importDefault(require("@inquirer/figures"));
/**
* Separator object
* Used to space/separate choices group
*/
class Separator {
separator = yoctocolors_cjs_1.default.dim(Array.from({ length: 15 }).join(figures_1.default.line));
type = 'separator';
constructor(separator) {
if (separator) {
this.separator = separator;
}
}
static isSeparator(choice) {
return Boolean(choice &&
typeof choice === 'object' &&
'type' in choice &&
choice.type === 'separator');
}
}
exports.Separator = Separator;

View File

@@ -0,0 +1,4 @@
import { type Prompt, type Prettify } from '@inquirer/type';
type ViewFunction<Value, Config> = (config: Prettify<Config>, done: (value: Value) => void) => string | [string, string | undefined];
export declare function createPrompt<Value, Config>(view: ViewFunction<Value, Config>): Prompt<Value, Config>;
export {};

View File

@@ -0,0 +1,156 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPrompt = createPrompt;
const readline = __importStar(require("node:readline"));
const node_async_hooks_1 = require("node:async_hooks");
const mute_stream_1 = __importDefault(require("mute-stream"));
const signal_exit_1 = require("signal-exit");
const screen_manager_ts_1 = __importDefault(require("./screen-manager.js"));
const promise_polyfill_ts_1 = require("./promise-polyfill.js");
const hook_engine_ts_1 = require("./hook-engine.js");
const errors_ts_1 = require("./errors.js");
function getCallSites() {
// eslint-disable-next-line @typescript-eslint/unbound-method
const _prepareStackTrace = Error.prepareStackTrace;
let result = [];
try {
Error.prepareStackTrace = (_, callSites) => {
const callSitesWithoutCurrent = callSites.slice(1);
result = callSitesWithoutCurrent;
return callSitesWithoutCurrent;
};
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
new Error().stack;
}
catch {
// An error will occur if the Node flag --frozen-intrinsics is used.
// https://nodejs.org/api/cli.html#--frozen-intrinsics
return result;
}
Error.prepareStackTrace = _prepareStackTrace;
return result;
}
function createPrompt(view) {
const callSites = getCallSites();
const prompt = (config, context = {}) => {
// Default `input` to stdin
const { input = process.stdin, signal } = context;
const cleanups = new Set();
// Add mute capabilities to the output
const output = new mute_stream_1.default();
output.pipe(context.output ?? process.stdout);
const rl = readline.createInterface({
terminal: true,
input,
output,
});
const screen = new screen_manager_ts_1.default(rl);
const { promise, resolve, reject } = promise_polyfill_ts_1.PromisePolyfill.withResolver();
const cancel = () => reject(new errors_ts_1.CancelPromptError());
if (signal) {
const abort = () => reject(new errors_ts_1.AbortPromptError({ cause: signal.reason }));
if (signal.aborted) {
abort();
return Object.assign(promise, { cancel });
}
signal.addEventListener('abort', abort);
cleanups.add(() => signal.removeEventListener('abort', abort));
}
cleanups.add((0, signal_exit_1.onExit)((code, signal) => {
reject(new errors_ts_1.ExitPromptError(`User force closed the prompt with ${code} ${signal}`));
}));
// SIGINT must be explicitly handled by the prompt so the ExitPromptError can be handled.
// Otherwise, the prompt will stop and in some scenarios never resolve.
// Ref issue #1741
const sigint = () => reject(new errors_ts_1.ExitPromptError(`User force closed the prompt with SIGINT`));
rl.on('SIGINT', sigint);
cleanups.add(() => rl.removeListener('SIGINT', sigint));
// Re-renders only happen when the state change; but the readline cursor could change position
// and that also requires a re-render (and a manual one because we mute the streams).
// We set the listener after the initial workLoop to avoid a double render if render triggered
// by a state change sets the cursor to the right position.
const checkCursorPos = () => screen.checkCursorPos();
rl.input.on('keypress', checkCursorPos);
cleanups.add(() => rl.input.removeListener('keypress', checkCursorPos));
return (0, hook_engine_ts_1.withHooks)(rl, (cycle) => {
// The close event triggers immediately when the user press ctrl+c. SignalExit on the other hand
// triggers after the process is done (which happens after timeouts are done triggering.)
// We triggers the hooks cleanup phase on rl `close` so active timeouts can be cleared.
const hooksCleanup = node_async_hooks_1.AsyncResource.bind(() => hook_engine_ts_1.effectScheduler.clearAll());
rl.on('close', hooksCleanup);
cleanups.add(() => rl.removeListener('close', hooksCleanup));
cycle(() => {
try {
const nextView = view(config, (value) => {
setImmediate(() => resolve(value));
});
// Typescript won't allow this, but not all users rely on typescript.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (nextView === undefined) {
const callerFilename = callSites[1]?.getFileName();
throw new Error(`Prompt functions must return a string.\n at ${callerFilename}`);
}
const [content, bottomContent] = typeof nextView === 'string' ? [nextView] : nextView;
screen.render(content, bottomContent);
hook_engine_ts_1.effectScheduler.run();
}
catch (error) {
reject(error);
}
});
return Object.assign(promise
.then((answer) => {
hook_engine_ts_1.effectScheduler.clearAll();
return answer;
}, (error) => {
hook_engine_ts_1.effectScheduler.clearAll();
throw error;
})
// Wait for the promise to settle, then cleanup.
.finally(() => {
cleanups.forEach((cleanup) => cleanup());
screen.done({ clearContent: Boolean(context.clearPromptOnDone) });
output.end();
})
// Once cleanup is done, let the expose promise resolve/reject to the internal one.
.then(() => promise), { cancel });
});
};
return prompt;
}

View File

@@ -0,0 +1,20 @@
export declare class AbortPromptError extends Error {
name: string;
message: string;
constructor(options?: {
cause?: unknown;
});
}
export declare class CancelPromptError extends Error {
name: string;
message: string;
}
export declare class ExitPromptError extends Error {
name: string;
}
export declare class HookError extends Error {
name: string;
}
export declare class ValidationError extends Error {
name: string;
}

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ValidationError = exports.HookError = exports.ExitPromptError = exports.CancelPromptError = exports.AbortPromptError = void 0;
class AbortPromptError extends Error {
name = 'AbortPromptError';
message = 'Prompt was aborted';
constructor(options) {
super();
this.cause = options?.cause;
}
}
exports.AbortPromptError = AbortPromptError;
class CancelPromptError extends Error {
name = 'CancelPromptError';
message = 'Prompt was canceled';
}
exports.CancelPromptError = CancelPromptError;
class ExitPromptError extends Error {
name = 'ExitPromptError';
}
exports.ExitPromptError = ExitPromptError;
class HookError extends Error {
name = 'HookError';
}
exports.HookError = HookError;
class ValidationError extends Error {
name = 'ValidationError';
}
exports.ValidationError = ValidationError;

View File

@@ -0,0 +1,23 @@
import type { InquirerReadline } from '@inquirer/type';
export declare function withHooks<T>(rl: InquirerReadline, cb: (cycle: (render: () => void) => void) => T): T;
export declare function readline(): InquirerReadline;
export declare function withUpdates<Args extends unknown[], R>(fn: (...args: Args) => R): (...args: Args) => R;
type SetPointer<Value> = {
get(): Value;
set(value: Value): void;
initialized: true;
};
type UnsetPointer<Value> = {
get(): void;
set(value: Value): void;
initialized: false;
};
type Pointer<Value> = SetPointer<Value> | UnsetPointer<Value>;
export declare function withPointer<Value, ReturnValue>(cb: (pointer: Pointer<Value>) => ReturnValue): ReturnValue;
export declare function handleChange(): void;
export declare const effectScheduler: {
queue(cb: (readline: InquirerReadline) => void | (() => void)): void;
run(): void;
clearAll(): void;
};
export {};

View File

@@ -0,0 +1,118 @@
"use strict";
/* eslint @typescript-eslint/no-explicit-any: ["off"] */
Object.defineProperty(exports, "__esModule", { value: true });
exports.effectScheduler = void 0;
exports.withHooks = withHooks;
exports.readline = readline;
exports.withUpdates = withUpdates;
exports.withPointer = withPointer;
exports.handleChange = handleChange;
const node_async_hooks_1 = require("node:async_hooks");
const errors_ts_1 = require("./errors.js");
const hookStorage = new node_async_hooks_1.AsyncLocalStorage();
function createStore(rl) {
const store = {
rl,
hooks: [],
hooksCleanup: [],
hooksEffect: [],
index: 0,
handleChange() { },
};
return store;
}
// Run callback in with the hook engine setup.
function withHooks(rl, cb) {
const store = createStore(rl);
return hookStorage.run(store, () => {
function cycle(render) {
store.handleChange = () => {
store.index = 0;
render();
};
store.handleChange();
}
return cb(cycle);
});
}
// Safe getStore utility that'll return the store or throw if undefined.
function getStore() {
const store = hookStorage.getStore();
if (!store) {
throw new errors_ts_1.HookError('[Inquirer] Hook functions can only be called from within a prompt');
}
return store;
}
function readline() {
return getStore().rl;
}
// Merge state updates happening within the callback function to avoid multiple renders.
function withUpdates(fn) {
const wrapped = (...args) => {
const store = getStore();
let shouldUpdate = false;
const oldHandleChange = store.handleChange;
store.handleChange = () => {
shouldUpdate = true;
};
const returnValue = fn(...args);
if (shouldUpdate) {
oldHandleChange();
}
store.handleChange = oldHandleChange;
return returnValue;
};
return node_async_hooks_1.AsyncResource.bind(wrapped);
}
function withPointer(cb) {
const store = getStore();
const { index } = store;
const pointer = {
get() {
return store.hooks[index];
},
set(value) {
store.hooks[index] = value;
},
initialized: index in store.hooks,
};
const returnValue = cb(pointer);
store.index++;
return returnValue;
}
function handleChange() {
getStore().handleChange();
}
exports.effectScheduler = {
queue(cb) {
const store = getStore();
const { index } = store;
store.hooksEffect.push(() => {
store.hooksCleanup[index]?.();
const cleanFn = cb(readline());
if (cleanFn != null && typeof cleanFn !== 'function') {
throw new errors_ts_1.ValidationError('useEffect return value must be a cleanup function or nothing.');
}
store.hooksCleanup[index] = cleanFn;
});
},
run() {
const store = getStore();
withUpdates(() => {
store.hooksEffect.forEach((effect) => {
effect();
});
// Warning: Clean the hooks before exiting the `withUpdates` block.
// Failure to do so means an updates would hit the same effects again.
store.hooksEffect.length = 0;
})();
},
clearAll() {
const store = getStore();
store.hooksCleanup.forEach((cleanFn) => {
cleanFn?.();
});
store.hooksEffect.length = 0;
store.hooksCleanup.length = 0;
},
};

View File

@@ -0,0 +1,12 @@
export type KeypressEvent = {
name: string;
ctrl: boolean;
};
export type Keybinding = 'emacs' | 'vim';
export declare const isUpKey: (key: KeypressEvent, keybindings?: ReadonlyArray<Keybinding>) => boolean;
export declare const isDownKey: (key: KeypressEvent, keybindings?: ReadonlyArray<Keybinding>) => boolean;
export declare const isSpaceKey: (key: KeypressEvent) => boolean;
export declare const isBackspaceKey: (key: KeypressEvent) => boolean;
export declare const isTabKey: (key: KeypressEvent) => boolean;
export declare const isNumberKey: (key: KeypressEvent) => boolean;
export declare const isEnterKey: (key: KeypressEvent) => boolean;

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isEnterKey = exports.isNumberKey = exports.isTabKey = exports.isBackspaceKey = exports.isSpaceKey = exports.isDownKey = exports.isUpKey = void 0;
const isUpKey = (key, keybindings = []) =>
// The up key
key.name === 'up' ||
// Vim keybinding: hjkl keys map to left/down/up/right
(keybindings.includes('vim') && key.name === 'k') ||
// Emacs keybinding: Ctrl+P means "previous" in Emacs navigation conventions
(keybindings.includes('emacs') && key.ctrl && key.name === 'p');
exports.isUpKey = isUpKey;
const isDownKey = (key, keybindings = []) =>
// The down key
key.name === 'down' ||
// Vim keybinding: hjkl keys map to left/down/up/right
(keybindings.includes('vim') && key.name === 'j') ||
// Emacs keybinding: Ctrl+N means "next" in Emacs navigation conventions
(keybindings.includes('emacs') && key.ctrl && key.name === 'n');
exports.isDownKey = isDownKey;
const isSpaceKey = (key) => key.name === 'space';
exports.isSpaceKey = isSpaceKey;
const isBackspaceKey = (key) => key.name === 'backspace';
exports.isBackspaceKey = isBackspaceKey;
const isTabKey = (key) => key.name === 'tab';
exports.isTabKey = isTabKey;
const isNumberKey = (key) => '1234567890'.includes(key.name);
exports.isNumberKey = isNumberKey;
const isEnterKey = (key) => key.name === 'enter' || key.name === 'return';
exports.isEnterKey = isEnterKey;

View File

@@ -0,0 +1,3 @@
import type { Prettify, PartialDeep } from '@inquirer/type';
import { type Theme } from './theme.ts';
export declare function makeTheme<SpecificTheme extends object>(...themes: ReadonlyArray<undefined | PartialDeep<Theme<SpecificTheme>>>): Prettify<Theme<SpecificTheme>>;

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeTheme = makeTheme;
const theme_ts_1 = require("./theme.js");
function isPlainObject(value) {
if (typeof value !== 'object' || value === null)
return false;
let proto = value;
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto);
}
return Object.getPrototypeOf(value) === proto;
}
function deepMerge(...objects) {
const output = {};
for (const obj of objects) {
for (const [key, value] of Object.entries(obj)) {
const prevValue = output[key];
output[key] =
isPlainObject(prevValue) && isPlainObject(value)
? deepMerge(prevValue, value)
: value;
}
}
return output;
}
function makeTheme(...themes) {
const themesToMerge = [
theme_ts_1.defaultTheme,
...themes.filter((theme) => theme != null),
];
return deepMerge(...themesToMerge);
}

View File

@@ -0,0 +1,16 @@
import type { Prettify } from '@inquirer/type';
export declare function usePagination<T>({ items, active, renderItem, pageSize, loop, }: {
items: ReadonlyArray<T>;
/** The index of the active item. */
active: number;
/** Renders an item as part of a page. */
renderItem: (layout: Prettify<{
item: T;
index: number;
isActive: boolean;
}>) => string;
/** The size of the page. */
pageSize: number;
/** Allows creating an infinitely looping list. `true` if unspecified. */
loop?: boolean;
}): string;

View File

@@ -0,0 +1,124 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePagination = usePagination;
const use_ref_ts_1 = require("../use-ref.js");
const utils_ts_1 = require("../utils.js");
function usePointerPosition({ active, renderedItems, pageSize, loop, }) {
const state = (0, use_ref_ts_1.useRef)({
lastPointer: active,
lastActive: undefined,
});
const { lastPointer, lastActive } = state.current;
const middle = Math.floor(pageSize / 2);
const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0);
const defaultPointerPosition = renderedItems
.slice(0, active)
.reduce((acc, item) => acc + item.length, 0);
let pointer = defaultPointerPosition;
if (renderedLength > pageSize) {
if (loop) {
/**
* Creates the next position for the pointer considering an infinitely
* looping list of items to be rendered on the page.
*
* The goal is to progressively move the cursor to the middle position as the user move down, and then keep
* the cursor there. When the user move up, maintain the cursor position.
*/
// By default, keep the cursor position as-is.
pointer = lastPointer;
if (
// First render, skip this logic.
lastActive != null &&
// Only move the pointer down when the user moves down.
lastActive < active &&
// Check user didn't move up across page boundary.
active - lastActive < pageSize) {
pointer = Math.min(
// Furthest allowed position for the pointer is the middle of the list
middle, Math.abs(active - lastActive) === 1
? Math.min(
// Move the pointer at most the height of the last active item.
lastPointer + (renderedItems[lastActive]?.length ?? 0),
// If the user moved by one item, move the pointer to the natural position of the active item as
// long as it doesn't move the cursor up.
Math.max(defaultPointerPosition, lastPointer))
: // Otherwise, move the pointer down by the difference between the active and last active item.
lastPointer + active - lastActive);
}
}
else {
/**
* Creates the next position for the pointer considering a finite list of
* items to be rendered on a page.
*
* The goal is to keep the pointer in the middle of the page whenever possible, until
* we reach the bounds of the list (top or bottom). In which case, the cursor moves progressively
* to the bottom or top of the list.
*/
const spaceUnderActive = renderedItems
.slice(active)
.reduce((acc, item) => acc + item.length, 0);
pointer =
spaceUnderActive < pageSize - middle
? // If the active item is near the end of the list, progressively move the cursor towards the end.
pageSize - spaceUnderActive
: // Otherwise, progressively move the pointer to the middle of the list.
Math.min(defaultPointerPosition, middle);
}
}
// Save state for the next render
state.current.lastPointer = pointer;
state.current.lastActive = active;
return pointer;
}
function usePagination({ items, active, renderItem, pageSize, loop = true, }) {
const width = (0, utils_ts_1.readlineWidth)();
const bound = (num) => ((num % items.length) + items.length) % items.length;
const renderedItems = items.map((item, index) => {
if (item == null)
return [];
return (0, utils_ts_1.breakLines)(renderItem({ item, index, isActive: index === active }), width).split('\n');
});
const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0);
const renderItemAtIndex = (index) => renderedItems[index] ?? [];
const pointer = usePointerPosition({ active, renderedItems, pageSize, loop });
// Render the active item to decide the position.
// If the active item fits under the pointer, we render it there.
// Otherwise, we need to render it to fit at the bottom of the page; moving the pointer up.
const activeItem = renderItemAtIndex(active).slice(0, pageSize);
const activeItemPosition = pointer + activeItem.length <= pageSize ? pointer : pageSize - activeItem.length;
// Create an array of lines for the page, and add the lines of the active item into the page
const pageBuffer = Array.from({ length: pageSize });
pageBuffer.splice(activeItemPosition, activeItem.length, ...activeItem);
// Store to prevent rendering the same item twice
const itemVisited = new Set([active]);
// Fill the page under the active item
let bufferPointer = activeItemPosition + activeItem.length;
let itemPointer = bound(active + 1);
while (bufferPointer < pageSize &&
!itemVisited.has(itemPointer) &&
(loop && renderedLength > pageSize ? itemPointer !== active : itemPointer > active)) {
const lines = renderItemAtIndex(itemPointer);
const linesToAdd = lines.slice(0, pageSize - bufferPointer);
pageBuffer.splice(bufferPointer, linesToAdd.length, ...linesToAdd);
// Move pointers for next iteration
itemVisited.add(itemPointer);
bufferPointer += linesToAdd.length;
itemPointer = bound(itemPointer + 1);
}
// Fill the page over the active item
bufferPointer = activeItemPosition - 1;
itemPointer = bound(active - 1);
while (bufferPointer >= 0 &&
!itemVisited.has(itemPointer) &&
(loop && renderedLength > pageSize ? itemPointer !== active : itemPointer < active)) {
const lines = renderItemAtIndex(itemPointer);
const linesToAdd = lines.slice(Math.max(0, lines.length - bufferPointer - 1));
pageBuffer.splice(bufferPointer - linesToAdd.length + 1, linesToAdd.length, ...linesToAdd);
// Move pointers for next iteration
itemVisited.add(itemPointer);
bufferPointer -= linesToAdd.length;
itemPointer = bound(itemPointer - 1);
}
return pageBuffer.filter((line) => typeof line === 'string').join('\n');
}

View File

@@ -0,0 +1,7 @@
export declare class PromisePolyfill<T> extends Promise<T> {
static withResolver<T>(): {
promise: Promise<T>;
resolve: (value: T) => void;
reject: (error: unknown) => void;
};
}

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PromisePolyfill = void 0;
// TODO: Remove this class once Node 22 becomes the minimum supported version.
class PromisePolyfill extends Promise {
// Available starting from Node 22
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
static withResolver() {
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve: resolve, reject: reject };
}
}
exports.PromisePolyfill = PromisePolyfill;

View File

@@ -0,0 +1,14 @@
import type { InquirerReadline } from '@inquirer/type';
export default class ScreenManager {
private height;
private extraLinesUnderPrompt;
private cursorPos;
private readonly rl;
constructor(rl: InquirerReadline);
write(content: string): void;
render(content: string, bottomContent?: string): void;
checkCursorPos(): void;
done({ clearContent }: {
clearContent: boolean;
}): void;
}

View File

@@ -0,0 +1,82 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const node_util_1 = require("node:util");
const utils_ts_1 = require("./utils.js");
const ansi_1 = require("@inquirer/ansi");
const height = (content) => content.split('\n').length;
const lastLine = (content) => content.split('\n').pop() ?? '';
class ScreenManager {
// These variables are keeping information to allow correct prompt re-rendering
height = 0;
extraLinesUnderPrompt = 0;
cursorPos;
rl;
constructor(rl) {
this.rl = rl;
this.cursorPos = rl.getCursorPos();
}
write(content) {
this.rl.output.unmute();
this.rl.output.write(content);
this.rl.output.mute();
}
render(content, bottomContent = '') {
// Write message to screen and setPrompt to control backspace
const promptLine = lastLine(content);
const rawPromptLine = (0, node_util_1.stripVTControlCharacters)(promptLine);
// Remove the rl.line from our prompt. We can't rely on the content of
// rl.line (mainly because of the password prompt), so just rely on it's
// length.
let prompt = rawPromptLine;
if (this.rl.line.length > 0) {
prompt = prompt.slice(0, -this.rl.line.length);
}
this.rl.setPrompt(prompt);
// SetPrompt will change cursor position, now we can get correct value
this.cursorPos = this.rl.getCursorPos();
const width = (0, utils_ts_1.readlineWidth)();
content = (0, utils_ts_1.breakLines)(content, width);
bottomContent = (0, utils_ts_1.breakLines)(bottomContent, width);
// Manually insert an extra line if we're at the end of the line.
// This prevent the cursor from appearing at the beginning of the
// current line.
if (rawPromptLine.length % width === 0) {
content += '\n';
}
let output = content + (bottomContent ? '\n' + bottomContent : '');
/**
* Re-adjust the cursor at the correct position.
*/
// We need to consider parts of the prompt under the cursor as part of the bottom
// content in order to correctly cleanup and re-render.
const promptLineUpDiff = Math.floor(rawPromptLine.length / width) - this.cursorPos.rows;
const bottomContentHeight = promptLineUpDiff + (bottomContent ? height(bottomContent) : 0);
// Return cursor to the input position (on top of the bottomContent)
if (bottomContentHeight > 0)
output += (0, ansi_1.cursorUp)(bottomContentHeight);
// Return cursor to the initial left offset.
output += (0, ansi_1.cursorTo)(this.cursorPos.cols);
/**
* Render and store state for future re-rendering
*/
this.write((0, ansi_1.cursorDown)(this.extraLinesUnderPrompt) + (0, ansi_1.eraseLines)(this.height) + output);
this.extraLinesUnderPrompt = bottomContentHeight;
this.height = height(output);
}
checkCursorPos() {
const cursorPos = this.rl.getCursorPos();
if (cursorPos.cols !== this.cursorPos.cols) {
this.write((0, ansi_1.cursorTo)(cursorPos.cols));
this.cursorPos = cursorPos;
}
}
done({ clearContent }) {
this.rl.setPrompt('');
let output = (0, ansi_1.cursorDown)(this.extraLinesUnderPrompt);
output += clearContent ? (0, ansi_1.eraseLines)(this.height) : '\n';
output += ansi_1.cursorShow;
this.write(output);
this.rl.close();
}
}
exports.default = ScreenManager;

View File

@@ -0,0 +1,155 @@
import type { Prettify } from '@inquirer/type';
/**
* Union type representing the possible statuses of a prompt.
*
* - `'loading'`: The prompt is currently loading.
* - `'idle'`: The prompt is loaded and currently waiting for the user to
* submit an answer.
* - `'done'`: The user has submitted an answer and the prompt is finished.
* - `string`: Any other string: The prompt is in a custom state.
*/
export type Status = 'loading' | 'idle' | 'done' | (string & {});
type DefaultTheme = {
/**
* Prefix to prepend to the message. If a function is provided, it will be
* called with the current status of the prompt, and the return value will be
* used as the prefix.
*
* @remarks
* If `status === 'loading'`, this property is ignored and the spinner (styled
* by the `spinner` property) will be displayed instead.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (status) => status === 'done' ? colors.green('✔') : colors.blue('?')
* ```
*/
prefix: string | Prettify<Omit<Record<Status, string>, 'loading'>>;
/**
* Configuration for the spinner that is displayed when the prompt is in the
* `'loading'` state.
*
* We recommend the use of {@link https://github.com/sindresorhus/cli-spinners|cli-spinners} for a list of available spinners.
*/
spinner: {
/**
* The time interval between frames, in milliseconds.
*
* @defaultValue
* ```ts
* 80
* ```
*/
interval: number;
/**
* A list of frames to show for the spinner.
*
* @defaultValue
* ```ts
* ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
* ```
*/
frames: string[];
};
/**
* Object containing functions to style different parts of the prompt.
*/
style: {
/**
* Style to apply to the user's answer once it has been submitted.
*
* @param text - The user's answer.
* @returns The styled answer.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text) => colors.cyan(text)
* ```
*/
answer: (text: string) => string;
/**
* Style to apply to the message displayed to the user.
*
* @param text - The message to style.
* @param status - The current status of the prompt.
* @returns The styled message.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text, status) => colors.bold(text)
* ```
*/
message: (text: string, status: Status) => string;
/**
* Style to apply to error messages.
*
* @param text - The error message.
* @returns The styled error message.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text) => colors.red(`> ${text}`)
* ```
*/
error: (text: string) => string;
/**
* Style to apply to the default answer when one is provided.
*
* @param text - The default answer.
* @returns The styled default answer.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text) => colors.dim(`(${text})`)
* ```
*/
defaultAnswer: (text: string) => string;
/**
* Style to apply to help text.
*
* @param text - The help text.
* @returns The styled help text.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text) => colors.dim(text)
* ```
*/
help: (text: string) => string;
/**
* Style to apply to highlighted text.
*
* @param text - The text to highlight.
* @returns The highlighted text.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text) => colors.cyan(text)
* ```
*/
highlight: (text: string) => string;
/**
* Style to apply to keyboard keys referred to in help texts.
*
* @param text - The key to style.
* @returns The styled key.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text) => colors.cyan(colors.bold(`<${text}>`))
* ```
*/
key: (text: string) => string;
};
};
export type Theme<Extension extends object = object> = Prettify<Extension & DefaultTheme>;
export declare const defaultTheme: DefaultTheme;
export {};

View File

@@ -0,0 +1,27 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.defaultTheme = void 0;
const yoctocolors_cjs_1 = __importDefault(require("yoctocolors-cjs"));
const figures_1 = __importDefault(require("@inquirer/figures"));
exports.defaultTheme = {
prefix: {
idle: yoctocolors_cjs_1.default.blue('?'),
done: yoctocolors_cjs_1.default.green(figures_1.default.tick),
},
spinner: {
interval: 80,
frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'].map((frame) => yoctocolors_cjs_1.default.yellow(frame)),
},
style: {
answer: yoctocolors_cjs_1.default.cyan,
message: yoctocolors_cjs_1.default.bold,
error: (text) => yoctocolors_cjs_1.default.red(`> ${text}`),
defaultAnswer: (text) => yoctocolors_cjs_1.default.dim(`(${text})`),
help: yoctocolors_cjs_1.default.dim,
highlight: yoctocolors_cjs_1.default.cyan,
key: (text) => yoctocolors_cjs_1.default.cyan(yoctocolors_cjs_1.default.bold(`<${text}>`)),
},
};

View File

@@ -0,0 +1,2 @@
import type { InquirerReadline } from '@inquirer/type';
export declare function useEffect(cb: (rl: InquirerReadline) => void | (() => void), depArray: ReadonlyArray<unknown>): void;

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useEffect = useEffect;
const hook_engine_ts_1 = require("./hook-engine.js");
function useEffect(cb, depArray) {
(0, hook_engine_ts_1.withPointer)((pointer) => {
const oldDeps = pointer.get();
const hasChanged = !Array.isArray(oldDeps) || depArray.some((dep, i) => !Object.is(dep, oldDeps[i]));
if (hasChanged) {
hook_engine_ts_1.effectScheduler.queue(cb);
}
pointer.set(depArray);
});
}

View File

@@ -0,0 +1,3 @@
import { type InquirerReadline } from '@inquirer/type';
import { type KeypressEvent } from './key.ts';
export declare function useKeypress(userHandler: (event: KeypressEvent, rl: InquirerReadline) => void | Promise<void>): void;

View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useKeypress = useKeypress;
const use_ref_ts_1 = require("./use-ref.js");
const use_effect_ts_1 = require("./use-effect.js");
const hook_engine_ts_1 = require("./hook-engine.js");
function useKeypress(userHandler) {
const signal = (0, use_ref_ts_1.useRef)(userHandler);
signal.current = userHandler;
(0, use_effect_ts_1.useEffect)((rl) => {
let ignore = false;
const handler = (0, hook_engine_ts_1.withUpdates)((_input, event) => {
if (ignore)
return;
void signal.current(event, rl);
});
rl.input.on('keypress', handler);
return () => {
ignore = true;
rl.input.removeListener('keypress', handler);
};
}, []);
}

View File

@@ -0,0 +1 @@
export declare function useMemo<Value>(fn: () => Value, dependencies: ReadonlyArray<unknown>): Value;

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useMemo = useMemo;
const hook_engine_ts_1 = require("./hook-engine.js");
function useMemo(fn, dependencies) {
return (0, hook_engine_ts_1.withPointer)((pointer) => {
const prev = pointer.get();
if (!prev ||
prev.dependencies.length !== dependencies.length ||
prev.dependencies.some((dep, i) => dep !== dependencies[i])) {
const value = fn();
pointer.set({ value, dependencies });
return value;
}
return prev.value;
});
}

View File

@@ -0,0 +1,5 @@
import type { Theme, Status } from './theme.ts';
export declare function usePrefix({ status, theme, }: {
status?: Status;
theme?: Theme;
}): string;

View File

@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePrefix = usePrefix;
const use_state_ts_1 = require("./use-state.js");
const use_effect_ts_1 = require("./use-effect.js");
const make_theme_ts_1 = require("./make-theme.js");
function usePrefix({ status = 'idle', theme, }) {
const [showLoader, setShowLoader] = (0, use_state_ts_1.useState)(false);
const [tick, setTick] = (0, use_state_ts_1.useState)(0);
const { prefix, spinner } = (0, make_theme_ts_1.makeTheme)(theme);
(0, use_effect_ts_1.useEffect)(() => {
if (status === 'loading') {
let tickInterval;
let inc = -1;
// Delay displaying spinner by 300ms, to avoid flickering
const delayTimeout = setTimeout(() => {
setShowLoader(true);
tickInterval = setInterval(() => {
inc = inc + 1;
setTick(inc % spinner.frames.length);
}, spinner.interval);
}, 300);
return () => {
clearTimeout(delayTimeout);
clearInterval(tickInterval);
};
}
else {
setShowLoader(false);
}
}, [status]);
if (showLoader) {
return spinner.frames[tick];
}
// There's a delay before we show the loader. So we want to ignore `loading` here, and pass idle instead.
const iconName = status === 'loading' ? 'idle' : status;
return typeof prefix === 'string' ? prefix : (prefix[iconName] ?? prefix['idle']);
}

View File

@@ -0,0 +1,6 @@
export declare function useRef<Value>(val: Value): {
current: Value;
};
export declare function useRef<Value>(val?: Value): {
current: Value | undefined;
};

View File

@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useRef = useRef;
const use_state_ts_1 = require("./use-state.js");
function useRef(val) {
return (0, use_state_ts_1.useState)({ current: val })[0];
}

View File

@@ -0,0 +1,4 @@
type NotFunction<T> = T extends (...args: never) => unknown ? never : T;
export declare function useState<Value>(defaultValue: NotFunction<Value> | (() => Value)): [Value, (newValue: Value) => void];
export declare function useState<Value>(defaultValue?: NotFunction<Value> | (() => Value)): [Value | undefined, (newValue?: Value) => void];
export {};

View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.useState = useState;
const node_async_hooks_1 = require("node:async_hooks");
const hook_engine_ts_1 = require("./hook-engine.js");
function useState(defaultValue) {
return (0, hook_engine_ts_1.withPointer)((pointer) => {
const setState = node_async_hooks_1.AsyncResource.bind(function setState(newValue) {
// Noop if the value is still the same.
if (pointer.get() !== newValue) {
pointer.set(newValue);
// Trigger re-render
(0, hook_engine_ts_1.handleChange)();
}
});
if (pointer.initialized) {
return [pointer.get(), setState];
}
const value = typeof defaultValue === 'function' ? defaultValue() : defaultValue;
pointer.set(value);
return [value, setState];
});
}

View File

@@ -0,0 +1,13 @@
/**
* Force line returns at specific width. This function is ANSI code friendly and it'll
* ignore invisible codes during width calculation.
* @param {string} content
* @param {number} width
* @return {string}
*/
export declare function breakLines(content: string, width: number): string;
/**
* Returns the width of the active readline, or 80 as default value.
* @returns {number}
*/
export declare function readlineWidth(): number;

View File

@@ -0,0 +1,32 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.breakLines = breakLines;
exports.readlineWidth = readlineWidth;
const cli_width_1 = __importDefault(require("cli-width"));
const wrap_ansi_1 = __importDefault(require("wrap-ansi"));
const hook_engine_ts_1 = require("./hook-engine.js");
/**
* Force line returns at specific width. This function is ANSI code friendly and it'll
* ignore invisible codes during width calculation.
* @param {string} content
* @param {number} width
* @return {string}
*/
function breakLines(content, width) {
return content
.split('\n')
.flatMap((line) => (0, wrap_ansi_1.default)(line, width, { trim: false, hard: true })
.split('\n')
.map((str) => str.trimEnd()))
.join('\n');
}
/**
* Returns the width of the active readline, or 80 as default value.
* @returns {number}
*/
function readlineWidth() {
return (0, cli_width_1.default)({ defaultWidth: 80, output: (0, hook_engine_ts_1.readline)().output });
}

View File

@@ -0,0 +1,3 @@
{
"type": "commonjs"
}

View File

@@ -0,0 +1,13 @@
export { isUpKey, isDownKey, isSpaceKey, isBackspaceKey, isTabKey, isNumberKey, isEnterKey, type KeypressEvent, type Keybinding, } from './lib/key.ts';
export * from './lib/errors.ts';
export { usePrefix } from './lib/use-prefix.ts';
export { useState } from './lib/use-state.ts';
export { useEffect } from './lib/use-effect.ts';
export { useMemo } from './lib/use-memo.ts';
export { useRef } from './lib/use-ref.ts';
export { useKeypress } from './lib/use-keypress.ts';
export { makeTheme } from './lib/make-theme.ts';
export type { Theme, Status } from './lib/theme.ts';
export { usePagination } from './lib/pagination/use-pagination.ts';
export { createPrompt } from './lib/create-prompt.ts';
export { Separator } from './lib/Separator.ts';

View File

@@ -0,0 +1,12 @@
export { isUpKey, isDownKey, isSpaceKey, isBackspaceKey, isTabKey, isNumberKey, isEnterKey, } from "./lib/key.js";
export * from "./lib/errors.js";
export { usePrefix } from "./lib/use-prefix.js";
export { useState } from "./lib/use-state.js";
export { useEffect } from "./lib/use-effect.js";
export { useMemo } from "./lib/use-memo.js";
export { useRef } from "./lib/use-ref.js";
export { useKeypress } from "./lib/use-keypress.js";
export { makeTheme } from "./lib/make-theme.js";
export { usePagination } from "./lib/pagination/use-pagination.js";
export { createPrompt } from "./lib/create-prompt.js";
export { Separator } from "./lib/Separator.js";

View File

@@ -0,0 +1,10 @@
/**
* Separator object
* Used to space/separate choices group
*/
export declare class Separator {
readonly separator: string;
readonly type: string;
constructor(separator?: string);
static isSeparator(choice: unknown): choice is Separator;
}

View File

@@ -0,0 +1,21 @@
import colors from 'yoctocolors-cjs';
import figures from '@inquirer/figures';
/**
* Separator object
* Used to space/separate choices group
*/
export class Separator {
separator = colors.dim(Array.from({ length: 15 }).join(figures.line));
type = 'separator';
constructor(separator) {
if (separator) {
this.separator = separator;
}
}
static isSeparator(choice) {
return Boolean(choice &&
typeof choice === 'object' &&
'type' in choice &&
choice.type === 'separator');
}
}

View File

@@ -0,0 +1,4 @@
import { type Prompt, type Prettify } from '@inquirer/type';
type ViewFunction<Value, Config> = (config: Prettify<Config>, done: (value: Value) => void) => string | [string, string | undefined];
export declare function createPrompt<Value, Config>(view: ViewFunction<Value, Config>): Prompt<Value, Config>;
export {};

View File

@@ -0,0 +1,117 @@
import * as readline from 'node:readline';
import { AsyncResource } from 'node:async_hooks';
import MuteStream from 'mute-stream';
import { onExit as onSignalExit } from 'signal-exit';
import ScreenManager from "./screen-manager.js";
import { PromisePolyfill } from "./promise-polyfill.js";
import { withHooks, effectScheduler } from "./hook-engine.js";
import { AbortPromptError, CancelPromptError, ExitPromptError } from "./errors.js";
function getCallSites() {
// eslint-disable-next-line @typescript-eslint/unbound-method
const _prepareStackTrace = Error.prepareStackTrace;
let result = [];
try {
Error.prepareStackTrace = (_, callSites) => {
const callSitesWithoutCurrent = callSites.slice(1);
result = callSitesWithoutCurrent;
return callSitesWithoutCurrent;
};
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
new Error().stack;
}
catch {
// An error will occur if the Node flag --frozen-intrinsics is used.
// https://nodejs.org/api/cli.html#--frozen-intrinsics
return result;
}
Error.prepareStackTrace = _prepareStackTrace;
return result;
}
export function createPrompt(view) {
const callSites = getCallSites();
const prompt = (config, context = {}) => {
// Default `input` to stdin
const { input = process.stdin, signal } = context;
const cleanups = new Set();
// Add mute capabilities to the output
const output = new MuteStream();
output.pipe(context.output ?? process.stdout);
const rl = readline.createInterface({
terminal: true,
input,
output,
});
const screen = new ScreenManager(rl);
const { promise, resolve, reject } = PromisePolyfill.withResolver();
const cancel = () => reject(new CancelPromptError());
if (signal) {
const abort = () => reject(new AbortPromptError({ cause: signal.reason }));
if (signal.aborted) {
abort();
return Object.assign(promise, { cancel });
}
signal.addEventListener('abort', abort);
cleanups.add(() => signal.removeEventListener('abort', abort));
}
cleanups.add(onSignalExit((code, signal) => {
reject(new ExitPromptError(`User force closed the prompt with ${code} ${signal}`));
}));
// SIGINT must be explicitly handled by the prompt so the ExitPromptError can be handled.
// Otherwise, the prompt will stop and in some scenarios never resolve.
// Ref issue #1741
const sigint = () => reject(new ExitPromptError(`User force closed the prompt with SIGINT`));
rl.on('SIGINT', sigint);
cleanups.add(() => rl.removeListener('SIGINT', sigint));
// Re-renders only happen when the state change; but the readline cursor could change position
// and that also requires a re-render (and a manual one because we mute the streams).
// We set the listener after the initial workLoop to avoid a double render if render triggered
// by a state change sets the cursor to the right position.
const checkCursorPos = () => screen.checkCursorPos();
rl.input.on('keypress', checkCursorPos);
cleanups.add(() => rl.input.removeListener('keypress', checkCursorPos));
return withHooks(rl, (cycle) => {
// The close event triggers immediately when the user press ctrl+c. SignalExit on the other hand
// triggers after the process is done (which happens after timeouts are done triggering.)
// We triggers the hooks cleanup phase on rl `close` so active timeouts can be cleared.
const hooksCleanup = AsyncResource.bind(() => effectScheduler.clearAll());
rl.on('close', hooksCleanup);
cleanups.add(() => rl.removeListener('close', hooksCleanup));
cycle(() => {
try {
const nextView = view(config, (value) => {
setImmediate(() => resolve(value));
});
// Typescript won't allow this, but not all users rely on typescript.
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (nextView === undefined) {
const callerFilename = callSites[1]?.getFileName();
throw new Error(`Prompt functions must return a string.\n at ${callerFilename}`);
}
const [content, bottomContent] = typeof nextView === 'string' ? [nextView] : nextView;
screen.render(content, bottomContent);
effectScheduler.run();
}
catch (error) {
reject(error);
}
});
return Object.assign(promise
.then((answer) => {
effectScheduler.clearAll();
return answer;
}, (error) => {
effectScheduler.clearAll();
throw error;
})
// Wait for the promise to settle, then cleanup.
.finally(() => {
cleanups.forEach((cleanup) => cleanup());
screen.done({ clearContent: Boolean(context.clearPromptOnDone) });
output.end();
})
// Once cleanup is done, let the expose promise resolve/reject to the internal one.
.then(() => promise), { cancel });
});
};
return prompt;
}

View File

@@ -0,0 +1,20 @@
export declare class AbortPromptError extends Error {
name: string;
message: string;
constructor(options?: {
cause?: unknown;
});
}
export declare class CancelPromptError extends Error {
name: string;
message: string;
}
export declare class ExitPromptError extends Error {
name: string;
}
export declare class HookError extends Error {
name: string;
}
export declare class ValidationError extends Error {
name: string;
}

View File

@@ -0,0 +1,21 @@
export class AbortPromptError extends Error {
name = 'AbortPromptError';
message = 'Prompt was aborted';
constructor(options) {
super();
this.cause = options?.cause;
}
}
export class CancelPromptError extends Error {
name = 'CancelPromptError';
message = 'Prompt was canceled';
}
export class ExitPromptError extends Error {
name = 'ExitPromptError';
}
export class HookError extends Error {
name = 'HookError';
}
export class ValidationError extends Error {
name = 'ValidationError';
}

View File

@@ -0,0 +1,23 @@
import type { InquirerReadline } from '@inquirer/type';
export declare function withHooks<T>(rl: InquirerReadline, cb: (cycle: (render: () => void) => void) => T): T;
export declare function readline(): InquirerReadline;
export declare function withUpdates<Args extends unknown[], R>(fn: (...args: Args) => R): (...args: Args) => R;
type SetPointer<Value> = {
get(): Value;
set(value: Value): void;
initialized: true;
};
type UnsetPointer<Value> = {
get(): void;
set(value: Value): void;
initialized: false;
};
type Pointer<Value> = SetPointer<Value> | UnsetPointer<Value>;
export declare function withPointer<Value, ReturnValue>(cb: (pointer: Pointer<Value>) => ReturnValue): ReturnValue;
export declare function handleChange(): void;
export declare const effectScheduler: {
queue(cb: (readline: InquirerReadline) => void | (() => void)): void;
run(): void;
clearAll(): void;
};
export {};

View File

@@ -0,0 +1,110 @@
/* eslint @typescript-eslint/no-explicit-any: ["off"] */
import { AsyncLocalStorage, AsyncResource } from 'node:async_hooks';
import { HookError, ValidationError } from "./errors.js";
const hookStorage = new AsyncLocalStorage();
function createStore(rl) {
const store = {
rl,
hooks: [],
hooksCleanup: [],
hooksEffect: [],
index: 0,
handleChange() { },
};
return store;
}
// Run callback in with the hook engine setup.
export function withHooks(rl, cb) {
const store = createStore(rl);
return hookStorage.run(store, () => {
function cycle(render) {
store.handleChange = () => {
store.index = 0;
render();
};
store.handleChange();
}
return cb(cycle);
});
}
// Safe getStore utility that'll return the store or throw if undefined.
function getStore() {
const store = hookStorage.getStore();
if (!store) {
throw new HookError('[Inquirer] Hook functions can only be called from within a prompt');
}
return store;
}
export function readline() {
return getStore().rl;
}
// Merge state updates happening within the callback function to avoid multiple renders.
export function withUpdates(fn) {
const wrapped = (...args) => {
const store = getStore();
let shouldUpdate = false;
const oldHandleChange = store.handleChange;
store.handleChange = () => {
shouldUpdate = true;
};
const returnValue = fn(...args);
if (shouldUpdate) {
oldHandleChange();
}
store.handleChange = oldHandleChange;
return returnValue;
};
return AsyncResource.bind(wrapped);
}
export function withPointer(cb) {
const store = getStore();
const { index } = store;
const pointer = {
get() {
return store.hooks[index];
},
set(value) {
store.hooks[index] = value;
},
initialized: index in store.hooks,
};
const returnValue = cb(pointer);
store.index++;
return returnValue;
}
export function handleChange() {
getStore().handleChange();
}
export const effectScheduler = {
queue(cb) {
const store = getStore();
const { index } = store;
store.hooksEffect.push(() => {
store.hooksCleanup[index]?.();
const cleanFn = cb(readline());
if (cleanFn != null && typeof cleanFn !== 'function') {
throw new ValidationError('useEffect return value must be a cleanup function or nothing.');
}
store.hooksCleanup[index] = cleanFn;
});
},
run() {
const store = getStore();
withUpdates(() => {
store.hooksEffect.forEach((effect) => {
effect();
});
// Warning: Clean the hooks before exiting the `withUpdates` block.
// Failure to do so means an updates would hit the same effects again.
store.hooksEffect.length = 0;
})();
},
clearAll() {
const store = getStore();
store.hooksCleanup.forEach((cleanFn) => {
cleanFn?.();
});
store.hooksEffect.length = 0;
store.hooksCleanup.length = 0;
},
};

View File

@@ -0,0 +1,12 @@
export type KeypressEvent = {
name: string;
ctrl: boolean;
};
export type Keybinding = 'emacs' | 'vim';
export declare const isUpKey: (key: KeypressEvent, keybindings?: ReadonlyArray<Keybinding>) => boolean;
export declare const isDownKey: (key: KeypressEvent, keybindings?: ReadonlyArray<Keybinding>) => boolean;
export declare const isSpaceKey: (key: KeypressEvent) => boolean;
export declare const isBackspaceKey: (key: KeypressEvent) => boolean;
export declare const isTabKey: (key: KeypressEvent) => boolean;
export declare const isNumberKey: (key: KeypressEvent) => boolean;
export declare const isEnterKey: (key: KeypressEvent) => boolean;

View File

@@ -0,0 +1,19 @@
export const isUpKey = (key, keybindings = []) =>
// The up key
key.name === 'up' ||
// Vim keybinding: hjkl keys map to left/down/up/right
(keybindings.includes('vim') && key.name === 'k') ||
// Emacs keybinding: Ctrl+P means "previous" in Emacs navigation conventions
(keybindings.includes('emacs') && key.ctrl && key.name === 'p');
export const isDownKey = (key, keybindings = []) =>
// The down key
key.name === 'down' ||
// Vim keybinding: hjkl keys map to left/down/up/right
(keybindings.includes('vim') && key.name === 'j') ||
// Emacs keybinding: Ctrl+N means "next" in Emacs navigation conventions
(keybindings.includes('emacs') && key.ctrl && key.name === 'n');
export const isSpaceKey = (key) => key.name === 'space';
export const isBackspaceKey = (key) => key.name === 'backspace';
export const isTabKey = (key) => key.name === 'tab';
export const isNumberKey = (key) => '1234567890'.includes(key.name);
export const isEnterKey = (key) => key.name === 'enter' || key.name === 'return';

View File

@@ -0,0 +1,3 @@
import type { Prettify, PartialDeep } from '@inquirer/type';
import { type Theme } from './theme.ts';
export declare function makeTheme<SpecificTheme extends object>(...themes: ReadonlyArray<undefined | PartialDeep<Theme<SpecificTheme>>>): Prettify<Theme<SpecificTheme>>;

View File

@@ -0,0 +1,30 @@
import { defaultTheme } from "./theme.js";
function isPlainObject(value) {
if (typeof value !== 'object' || value === null)
return false;
let proto = value;
while (Object.getPrototypeOf(proto) !== null) {
proto = Object.getPrototypeOf(proto);
}
return Object.getPrototypeOf(value) === proto;
}
function deepMerge(...objects) {
const output = {};
for (const obj of objects) {
for (const [key, value] of Object.entries(obj)) {
const prevValue = output[key];
output[key] =
isPlainObject(prevValue) && isPlainObject(value)
? deepMerge(prevValue, value)
: value;
}
}
return output;
}
export function makeTheme(...themes) {
const themesToMerge = [
defaultTheme,
...themes.filter((theme) => theme != null),
];
return deepMerge(...themesToMerge);
}

View File

@@ -0,0 +1,16 @@
import type { Prettify } from '@inquirer/type';
export declare function usePagination<T>({ items, active, renderItem, pageSize, loop, }: {
items: ReadonlyArray<T>;
/** The index of the active item. */
active: number;
/** Renders an item as part of a page. */
renderItem: (layout: Prettify<{
item: T;
index: number;
isActive: boolean;
}>) => string;
/** The size of the page. */
pageSize: number;
/** Allows creating an infinitely looping list. `true` if unspecified. */
loop?: boolean;
}): string;

View File

@@ -0,0 +1,121 @@
import { useRef } from "../use-ref.js";
import { readlineWidth, breakLines } from "../utils.js";
function usePointerPosition({ active, renderedItems, pageSize, loop, }) {
const state = useRef({
lastPointer: active,
lastActive: undefined,
});
const { lastPointer, lastActive } = state.current;
const middle = Math.floor(pageSize / 2);
const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0);
const defaultPointerPosition = renderedItems
.slice(0, active)
.reduce((acc, item) => acc + item.length, 0);
let pointer = defaultPointerPosition;
if (renderedLength > pageSize) {
if (loop) {
/**
* Creates the next position for the pointer considering an infinitely
* looping list of items to be rendered on the page.
*
* The goal is to progressively move the cursor to the middle position as the user move down, and then keep
* the cursor there. When the user move up, maintain the cursor position.
*/
// By default, keep the cursor position as-is.
pointer = lastPointer;
if (
// First render, skip this logic.
lastActive != null &&
// Only move the pointer down when the user moves down.
lastActive < active &&
// Check user didn't move up across page boundary.
active - lastActive < pageSize) {
pointer = Math.min(
// Furthest allowed position for the pointer is the middle of the list
middle, Math.abs(active - lastActive) === 1
? Math.min(
// Move the pointer at most the height of the last active item.
lastPointer + (renderedItems[lastActive]?.length ?? 0),
// If the user moved by one item, move the pointer to the natural position of the active item as
// long as it doesn't move the cursor up.
Math.max(defaultPointerPosition, lastPointer))
: // Otherwise, move the pointer down by the difference between the active and last active item.
lastPointer + active - lastActive);
}
}
else {
/**
* Creates the next position for the pointer considering a finite list of
* items to be rendered on a page.
*
* The goal is to keep the pointer in the middle of the page whenever possible, until
* we reach the bounds of the list (top or bottom). In which case, the cursor moves progressively
* to the bottom or top of the list.
*/
const spaceUnderActive = renderedItems
.slice(active)
.reduce((acc, item) => acc + item.length, 0);
pointer =
spaceUnderActive < pageSize - middle
? // If the active item is near the end of the list, progressively move the cursor towards the end.
pageSize - spaceUnderActive
: // Otherwise, progressively move the pointer to the middle of the list.
Math.min(defaultPointerPosition, middle);
}
}
// Save state for the next render
state.current.lastPointer = pointer;
state.current.lastActive = active;
return pointer;
}
export function usePagination({ items, active, renderItem, pageSize, loop = true, }) {
const width = readlineWidth();
const bound = (num) => ((num % items.length) + items.length) % items.length;
const renderedItems = items.map((item, index) => {
if (item == null)
return [];
return breakLines(renderItem({ item, index, isActive: index === active }), width).split('\n');
});
const renderedLength = renderedItems.reduce((acc, item) => acc + item.length, 0);
const renderItemAtIndex = (index) => renderedItems[index] ?? [];
const pointer = usePointerPosition({ active, renderedItems, pageSize, loop });
// Render the active item to decide the position.
// If the active item fits under the pointer, we render it there.
// Otherwise, we need to render it to fit at the bottom of the page; moving the pointer up.
const activeItem = renderItemAtIndex(active).slice(0, pageSize);
const activeItemPosition = pointer + activeItem.length <= pageSize ? pointer : pageSize - activeItem.length;
// Create an array of lines for the page, and add the lines of the active item into the page
const pageBuffer = Array.from({ length: pageSize });
pageBuffer.splice(activeItemPosition, activeItem.length, ...activeItem);
// Store to prevent rendering the same item twice
const itemVisited = new Set([active]);
// Fill the page under the active item
let bufferPointer = activeItemPosition + activeItem.length;
let itemPointer = bound(active + 1);
while (bufferPointer < pageSize &&
!itemVisited.has(itemPointer) &&
(loop && renderedLength > pageSize ? itemPointer !== active : itemPointer > active)) {
const lines = renderItemAtIndex(itemPointer);
const linesToAdd = lines.slice(0, pageSize - bufferPointer);
pageBuffer.splice(bufferPointer, linesToAdd.length, ...linesToAdd);
// Move pointers for next iteration
itemVisited.add(itemPointer);
bufferPointer += linesToAdd.length;
itemPointer = bound(itemPointer + 1);
}
// Fill the page over the active item
bufferPointer = activeItemPosition - 1;
itemPointer = bound(active - 1);
while (bufferPointer >= 0 &&
!itemVisited.has(itemPointer) &&
(loop && renderedLength > pageSize ? itemPointer !== active : itemPointer < active)) {
const lines = renderItemAtIndex(itemPointer);
const linesToAdd = lines.slice(Math.max(0, lines.length - bufferPointer - 1));
pageBuffer.splice(bufferPointer - linesToAdd.length + 1, linesToAdd.length, ...linesToAdd);
// Move pointers for next iteration
itemVisited.add(itemPointer);
bufferPointer -= linesToAdd.length;
itemPointer = bound(itemPointer - 1);
}
return pageBuffer.filter((line) => typeof line === 'string').join('\n');
}

View File

@@ -0,0 +1,7 @@
export declare class PromisePolyfill<T> extends Promise<T> {
static withResolver<T>(): {
promise: Promise<T>;
resolve: (value: T) => void;
reject: (error: unknown) => void;
};
}

View File

@@ -0,0 +1,14 @@
// TODO: Remove this class once Node 22 becomes the minimum supported version.
export class PromisePolyfill extends Promise {
// Available starting from Node 22
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers
static withResolver() {
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
return { promise, resolve: resolve, reject: reject };
}
}

View File

@@ -0,0 +1,14 @@
import type { InquirerReadline } from '@inquirer/type';
export default class ScreenManager {
private height;
private extraLinesUnderPrompt;
private cursorPos;
private readonly rl;
constructor(rl: InquirerReadline);
write(content: string): void;
render(content: string, bottomContent?: string): void;
checkCursorPos(): void;
done({ clearContent }: {
clearContent: boolean;
}): void;
}

View File

@@ -0,0 +1,79 @@
import { stripVTControlCharacters } from 'node:util';
import { breakLines, readlineWidth } from "./utils.js";
import { cursorDown, cursorUp, cursorTo, cursorShow, eraseLines } from '@inquirer/ansi';
const height = (content) => content.split('\n').length;
const lastLine = (content) => content.split('\n').pop() ?? '';
export default class ScreenManager {
// These variables are keeping information to allow correct prompt re-rendering
height = 0;
extraLinesUnderPrompt = 0;
cursorPos;
rl;
constructor(rl) {
this.rl = rl;
this.cursorPos = rl.getCursorPos();
}
write(content) {
this.rl.output.unmute();
this.rl.output.write(content);
this.rl.output.mute();
}
render(content, bottomContent = '') {
// Write message to screen and setPrompt to control backspace
const promptLine = lastLine(content);
const rawPromptLine = stripVTControlCharacters(promptLine);
// Remove the rl.line from our prompt. We can't rely on the content of
// rl.line (mainly because of the password prompt), so just rely on it's
// length.
let prompt = rawPromptLine;
if (this.rl.line.length > 0) {
prompt = prompt.slice(0, -this.rl.line.length);
}
this.rl.setPrompt(prompt);
// SetPrompt will change cursor position, now we can get correct value
this.cursorPos = this.rl.getCursorPos();
const width = readlineWidth();
content = breakLines(content, width);
bottomContent = breakLines(bottomContent, width);
// Manually insert an extra line if we're at the end of the line.
// This prevent the cursor from appearing at the beginning of the
// current line.
if (rawPromptLine.length % width === 0) {
content += '\n';
}
let output = content + (bottomContent ? '\n' + bottomContent : '');
/**
* Re-adjust the cursor at the correct position.
*/
// We need to consider parts of the prompt under the cursor as part of the bottom
// content in order to correctly cleanup and re-render.
const promptLineUpDiff = Math.floor(rawPromptLine.length / width) - this.cursorPos.rows;
const bottomContentHeight = promptLineUpDiff + (bottomContent ? height(bottomContent) : 0);
// Return cursor to the input position (on top of the bottomContent)
if (bottomContentHeight > 0)
output += cursorUp(bottomContentHeight);
// Return cursor to the initial left offset.
output += cursorTo(this.cursorPos.cols);
/**
* Render and store state for future re-rendering
*/
this.write(cursorDown(this.extraLinesUnderPrompt) + eraseLines(this.height) + output);
this.extraLinesUnderPrompt = bottomContentHeight;
this.height = height(output);
}
checkCursorPos() {
const cursorPos = this.rl.getCursorPos();
if (cursorPos.cols !== this.cursorPos.cols) {
this.write(cursorTo(cursorPos.cols));
this.cursorPos = cursorPos;
}
}
done({ clearContent }) {
this.rl.setPrompt('');
let output = cursorDown(this.extraLinesUnderPrompt);
output += clearContent ? eraseLines(this.height) : '\n';
output += cursorShow;
this.write(output);
this.rl.close();
}
}

View File

@@ -0,0 +1,155 @@
import type { Prettify } from '@inquirer/type';
/**
* Union type representing the possible statuses of a prompt.
*
* - `'loading'`: The prompt is currently loading.
* - `'idle'`: The prompt is loaded and currently waiting for the user to
* submit an answer.
* - `'done'`: The user has submitted an answer and the prompt is finished.
* - `string`: Any other string: The prompt is in a custom state.
*/
export type Status = 'loading' | 'idle' | 'done' | (string & {});
type DefaultTheme = {
/**
* Prefix to prepend to the message. If a function is provided, it will be
* called with the current status of the prompt, and the return value will be
* used as the prefix.
*
* @remarks
* If `status === 'loading'`, this property is ignored and the spinner (styled
* by the `spinner` property) will be displayed instead.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (status) => status === 'done' ? colors.green('✔') : colors.blue('?')
* ```
*/
prefix: string | Prettify<Omit<Record<Status, string>, 'loading'>>;
/**
* Configuration for the spinner that is displayed when the prompt is in the
* `'loading'` state.
*
* We recommend the use of {@link https://github.com/sindresorhus/cli-spinners|cli-spinners} for a list of available spinners.
*/
spinner: {
/**
* The time interval between frames, in milliseconds.
*
* @defaultValue
* ```ts
* 80
* ```
*/
interval: number;
/**
* A list of frames to show for the spinner.
*
* @defaultValue
* ```ts
* ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']
* ```
*/
frames: string[];
};
/**
* Object containing functions to style different parts of the prompt.
*/
style: {
/**
* Style to apply to the user's answer once it has been submitted.
*
* @param text - The user's answer.
* @returns The styled answer.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text) => colors.cyan(text)
* ```
*/
answer: (text: string) => string;
/**
* Style to apply to the message displayed to the user.
*
* @param text - The message to style.
* @param status - The current status of the prompt.
* @returns The styled message.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text, status) => colors.bold(text)
* ```
*/
message: (text: string, status: Status) => string;
/**
* Style to apply to error messages.
*
* @param text - The error message.
* @returns The styled error message.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text) => colors.red(`> ${text}`)
* ```
*/
error: (text: string) => string;
/**
* Style to apply to the default answer when one is provided.
*
* @param text - The default answer.
* @returns The styled default answer.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text) => colors.dim(`(${text})`)
* ```
*/
defaultAnswer: (text: string) => string;
/**
* Style to apply to help text.
*
* @param text - The help text.
* @returns The styled help text.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text) => colors.dim(text)
* ```
*/
help: (text: string) => string;
/**
* Style to apply to highlighted text.
*
* @param text - The text to highlight.
* @returns The highlighted text.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text) => colors.cyan(text)
* ```
*/
highlight: (text: string) => string;
/**
* Style to apply to keyboard keys referred to in help texts.
*
* @param text - The key to style.
* @returns The styled key.
*
* @defaultValue
* ```ts
* // import colors from 'yoctocolors-cjs';
* (text) => colors.cyan(colors.bold(`<${text}>`))
* ```
*/
key: (text: string) => string;
};
};
export type Theme<Extension extends object = object> = Prettify<Extension & DefaultTheme>;
export declare const defaultTheme: DefaultTheme;
export {};

View File

@@ -0,0 +1,21 @@
import colors from 'yoctocolors-cjs';
import figures from '@inquirer/figures';
export const defaultTheme = {
prefix: {
idle: colors.blue('?'),
done: colors.green(figures.tick),
},
spinner: {
interval: 80,
frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'].map((frame) => colors.yellow(frame)),
},
style: {
answer: colors.cyan,
message: colors.bold,
error: (text) => colors.red(`> ${text}`),
defaultAnswer: (text) => colors.dim(`(${text})`),
help: colors.dim,
highlight: colors.cyan,
key: (text) => colors.cyan(colors.bold(`<${text}>`)),
},
};

View File

@@ -0,0 +1,2 @@
import type { InquirerReadline } from '@inquirer/type';
export declare function useEffect(cb: (rl: InquirerReadline) => void | (() => void), depArray: ReadonlyArray<unknown>): void;

View File

@@ -0,0 +1,11 @@
import { withPointer, effectScheduler } from "./hook-engine.js";
export function useEffect(cb, depArray) {
withPointer((pointer) => {
const oldDeps = pointer.get();
const hasChanged = !Array.isArray(oldDeps) || depArray.some((dep, i) => !Object.is(dep, oldDeps[i]));
if (hasChanged) {
effectScheduler.queue(cb);
}
pointer.set(depArray);
});
}

View File

@@ -0,0 +1,3 @@
import { type InquirerReadline } from '@inquirer/type';
import { type KeypressEvent } from './key.ts';
export declare function useKeypress(userHandler: (event: KeypressEvent, rl: InquirerReadline) => void | Promise<void>): void;

View File

@@ -0,0 +1,20 @@
import { useRef } from "./use-ref.js";
import { useEffect } from "./use-effect.js";
import { withUpdates } from "./hook-engine.js";
export function useKeypress(userHandler) {
const signal = useRef(userHandler);
signal.current = userHandler;
useEffect((rl) => {
let ignore = false;
const handler = withUpdates((_input, event) => {
if (ignore)
return;
void signal.current(event, rl);
});
rl.input.on('keypress', handler);
return () => {
ignore = true;
rl.input.removeListener('keypress', handler);
};
}, []);
}

View File

@@ -0,0 +1 @@
export declare function useMemo<Value>(fn: () => Value, dependencies: ReadonlyArray<unknown>): Value;

Some files were not shown because too many files have changed in this diff Show More