@franzzemen/brokenstock-shell 0.10.3 → 0.10.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,27 +1,25 @@
|
|
|
1
|
-
import { ZodType } from
|
|
2
|
-
import * as
|
|
3
|
-
import {
|
|
4
|
-
import { CanActivateFn, Router } from
|
|
5
|
-
import { HttpInterceptorFn } from
|
|
6
|
-
import {
|
|
7
|
-
import { BearerToken } from
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
result: CommandResult;
|
|
24
|
-
repeatCount?: number;
|
|
1
|
+
import { ZodType } from "zod/v4";
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
import { ElementRef, EnvironmentProviders, InjectionToken, OnDestroy, OnInit, Signal } from "@angular/core";
|
|
4
|
+
import { CanActivateFn, Router } from "@angular/router";
|
|
5
|
+
import { HttpInterceptorFn } from "@angular/common/http";
|
|
6
|
+
import { AbstractControl, ValidationErrors, Validator } from "@angular/forms";
|
|
7
|
+
import { BearerToken } from "@franzzemen/utility";
|
|
8
|
+
import { Credentials, SessionSummary, UserSession } from "@franzzemen/identity";
|
|
9
|
+
import { AutofillEvent } from "@angular/cdk/text-field";
|
|
10
|
+
import { MatDialog, MatDialogRef } from "@angular/material/dialog";
|
|
11
|
+
export type Severity = 'error' | 'warning' | 'info';
|
|
12
|
+
export interface CommandResult {
|
|
13
|
+
success: boolean;
|
|
14
|
+
message: string;
|
|
15
|
+
timestamp: Date;
|
|
16
|
+
severity?: Severity;
|
|
17
|
+
isHelp?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export interface CommandHistoryEntry {
|
|
20
|
+
input: string;
|
|
21
|
+
result: CommandResult;
|
|
22
|
+
repeatCount?: number;
|
|
25
23
|
}
|
|
26
24
|
/**
|
|
27
25
|
* An alternate name for a command. Mirrors `CommandAlias` in
|
|
@@ -39,9 +37,9 @@ interface CommandHistoryEntry {
|
|
|
39
37
|
* The real name is what `help` lists; alternates are named in the command's own
|
|
40
38
|
* usage text. The registry refuses a name already taken, real or alternate.
|
|
41
39
|
*/
|
|
42
|
-
interface CommandAlias {
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
export interface CommandAlias {
|
|
41
|
+
readonly name: string;
|
|
42
|
+
readonly prependArgs?: readonly string[];
|
|
45
43
|
}
|
|
46
44
|
/**
|
|
47
45
|
* Where a command belongs. Mirrors `@franzzemen/brokenstock-lexicon`'s
|
|
@@ -54,36 +52,36 @@ interface CommandAlias {
|
|
|
54
52
|
* - `'refuse'`: the command depends on state only its screen sets; typed
|
|
55
53
|
* anywhere else the shell refuses with a message naming the screen.
|
|
56
54
|
*/
|
|
57
|
-
interface RouteScope {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
64
|
-
type CommandScope = 'global' | RouteScope;
|
|
55
|
+
export interface RouteScope {
|
|
56
|
+
/** "the Trades screen" — used verbatim in refusals and in `help`. */
|
|
57
|
+
readonly screen: string;
|
|
58
|
+
/** Route patterns; see {@link matchesRoute}. */
|
|
59
|
+
readonly routes: readonly string[];
|
|
60
|
+
readonly elsewhere: 'navigate' | 'refuse';
|
|
61
|
+
}
|
|
62
|
+
export type CommandScope = 'global' | RouteScope;
|
|
65
63
|
/**
|
|
66
64
|
* Does `url` fall inside any of `patterns`? Query string and fragment are
|
|
67
65
|
* stripped; a pattern matches when the path equals it or continues past it at
|
|
68
66
|
* a `/`; a `:param` segment matches any one non-empty segment. Same rule as
|
|
69
67
|
* the lexicon's `matchesRoute`, tested there and here.
|
|
70
68
|
*/
|
|
71
|
-
declare function matchesRoute(url: string, patterns: readonly string[]): boolean;
|
|
69
|
+
export declare function matchesRoute(url: string, patterns: readonly string[]): boolean;
|
|
72
70
|
/**
|
|
73
71
|
* Lexicon entry: the platform-agnostic definition of a command.
|
|
74
72
|
* Execution is provided by each consumer (Angular app, CLI app, etc.).
|
|
75
73
|
*/
|
|
76
|
-
interface CommandLexiconEntry {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
74
|
+
export interface CommandLexiconEntry {
|
|
75
|
+
readonly name: string;
|
|
76
|
+
readonly description: string;
|
|
77
|
+
readonly usage: string;
|
|
78
|
+
readonly requiresAuth: boolean;
|
|
79
|
+
readonly argsSchema: ZodType;
|
|
80
|
+
/** Where the command belongs. Required: every command declares it. */
|
|
81
|
+
readonly scope: CommandScope;
|
|
82
|
+
/** Alternate names for this command. See {@link CommandAlias}. */
|
|
83
|
+
readonly aliases?: readonly CommandAlias[];
|
|
84
|
+
sanitizeInput?(input: string): string;
|
|
87
85
|
}
|
|
88
86
|
/**
|
|
89
87
|
* Result of a command's readiness check. `reason` is surfaced verbatim to the
|
|
@@ -91,86 +89,84 @@ interface CommandLexiconEntry {
|
|
|
91
89
|
* at all for an empty message, which would look like the command silently did
|
|
92
90
|
* nothing.
|
|
93
91
|
*/
|
|
94
|
-
interface CommandAvailability {
|
|
95
|
-
|
|
96
|
-
|
|
92
|
+
export interface CommandAvailability {
|
|
93
|
+
ok: boolean;
|
|
94
|
+
reason?: string;
|
|
97
95
|
}
|
|
98
96
|
/**
|
|
99
97
|
* Full command definition including execution.
|
|
100
98
|
*/
|
|
101
|
-
interface CommandDefinition extends CommandLexiconEntry {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
declare const
|
|
166
|
-
declare const SHELL_MENU_ITEMS: InjectionToken<MenuItem[]>;
|
|
167
|
-
declare const SHELL_AUTH_URLS: InjectionToken<ShellAuthUrls>;
|
|
99
|
+
export interface CommandDefinition extends CommandLexiconEntry {
|
|
100
|
+
/**
|
|
101
|
+
* Declarative readiness gate, checked centrally before `execute`. Use for
|
|
102
|
+
* preconditions the user can act on (entitlement, onboarding state) rather
|
|
103
|
+
* than checking imperatively as the first statement of `execute` — the
|
|
104
|
+
* framework can only annotate `help` for gates it can see.
|
|
105
|
+
*
|
|
106
|
+
* A command that fails this check stays listed in `help` and in
|
|
107
|
+
* tab-completion, annotated with its reason. Hiding it would read as a broken
|
|
108
|
+
* CLI; an explained refusal reads as guidance.
|
|
109
|
+
*
|
|
110
|
+
* Omit for always-available.
|
|
111
|
+
*/
|
|
112
|
+
isAvailable?(): CommandAvailability;
|
|
113
|
+
execute(args: string[]): CommandResult | Promise<CommandResult>;
|
|
114
|
+
}
|
|
115
|
+
export interface MenuItem {
|
|
116
|
+
icon: string;
|
|
117
|
+
label: string;
|
|
118
|
+
route: string;
|
|
119
|
+
command?: string;
|
|
120
|
+
/**
|
|
121
|
+
* ENTITLEMENT: "your plan includes this". False hides the item entirely.
|
|
122
|
+
* Omit for always-visible.
|
|
123
|
+
*/
|
|
124
|
+
visible?: () => boolean;
|
|
125
|
+
/**
|
|
126
|
+
* READINESS: "you have the data this screen needs". False renders the item
|
|
127
|
+
* greyed and inert rather than hiding it — the user keeps seeing that the
|
|
128
|
+
* feature exists, and `disabledReason` tells them how to unlock it.
|
|
129
|
+
*
|
|
130
|
+
* Deliberately a SIBLING to `visible`, never folded into it: entitlement and
|
|
131
|
+
* readiness are different axes, and collapsing them loses the
|
|
132
|
+
* hidden-vs-greyed distinction that makes a locked item self-explaining.
|
|
133
|
+
*
|
|
134
|
+
* Omit for always-enabled.
|
|
135
|
+
*/
|
|
136
|
+
enabled?: () => boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Tooltip shown in place of `label` while `enabled()` is false. Must name the
|
|
139
|
+
* prerequisite ("Create a portfolio first"), because it is the ONLY channel
|
|
140
|
+
* through which a user learns why the icon is dead.
|
|
141
|
+
*/
|
|
142
|
+
disabledReason?: () => string;
|
|
143
|
+
/**
|
|
144
|
+
* ATTENTION: something on this screen is waiting for the user ("2 imports waiting for you to
|
|
145
|
+
* identify a security"). A count drawn on the icon, and `tooltip` replacing the label's tooltip.
|
|
146
|
+
* Null, or a count of 0, draws nothing. Read from the template, so a signal-backed count updates live.
|
|
147
|
+
*
|
|
148
|
+
* Omit for an item that never needs attention.
|
|
149
|
+
*/
|
|
150
|
+
badge?: () => MenuBadge | null;
|
|
151
|
+
}
|
|
152
|
+
export interface MenuBadge {
|
|
153
|
+
count: number;
|
|
154
|
+
tooltip: string;
|
|
155
|
+
}
|
|
156
|
+
export interface ShellAuthUrls {
|
|
157
|
+
login: string;
|
|
158
|
+
logout: string;
|
|
159
|
+
refresh: string;
|
|
160
|
+
}
|
|
161
|
+
export declare const SHELL_APP_NAME: InjectionToken<string>;
|
|
162
|
+
export declare const SHELL_MENU_ITEMS: InjectionToken<MenuItem[]>;
|
|
163
|
+
export declare const SHELL_AUTH_URLS: InjectionToken<ShellAuthUrls>;
|
|
168
164
|
/**
|
|
169
165
|
* App-specific commands to register alongside the built-in command set.
|
|
170
166
|
* Provide via provideShellConfig().
|
|
171
167
|
*/
|
|
172
|
-
declare const SHELL_COMMANDS: InjectionToken<CommandDefinition[]>;
|
|
173
|
-
declare const SHELL_POST_LOGIN_ROUTE: InjectionToken<string>;
|
|
168
|
+
export declare const SHELL_COMMANDS: InjectionToken<CommandDefinition[]>;
|
|
169
|
+
export declare const SHELL_POST_LOGIN_ROUTE: InjectionToken<string>;
|
|
174
170
|
/**
|
|
175
171
|
* Whether this app shares one session across every window of the browser
|
|
176
172
|
* (multiple-windows PRD D2/D7).
|
|
@@ -182,88 +178,87 @@ declare const SHELL_POST_LOGIN_ROUTE: InjectionToken<string>;
|
|
|
182
178
|
* different exposure — extending this to them is a separate decision, not a
|
|
183
179
|
* default.
|
|
184
180
|
*/
|
|
185
|
-
declare const SHELL_SHARED_SESSION: InjectionToken<boolean>;
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
declare
|
|
199
|
-
|
|
200
|
-
declare const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
}
|
|
212
|
-
|
|
181
|
+
export declare const SHELL_SHARED_SESSION: InjectionToken<boolean>;
|
|
182
|
+
export interface ShellConfig {
|
|
183
|
+
appName: string;
|
|
184
|
+
menuItems: MenuItem[];
|
|
185
|
+
authUrls: ShellAuthUrls;
|
|
186
|
+
commands?: CommandDefinition[];
|
|
187
|
+
/**
|
|
188
|
+
* Share one session across every window of the browser. Defaults to false.
|
|
189
|
+
* See {@link SHELL_SHARED_SESSION} for why this is opt-in.
|
|
190
|
+
*/
|
|
191
|
+
sharedSession?: boolean;
|
|
192
|
+
}
|
|
193
|
+
export declare function provideShellConfig(config: ShellConfig): EnvironmentProviders;
|
|
194
|
+
export declare const authGuard: CanActivateFn;
|
|
195
|
+
export declare const guestGuard: CanActivateFn;
|
|
196
|
+
export declare const authInterceptor: HttpInterceptorFn;
|
|
197
|
+
export declare class RegexValidatorDirective implements Validator {
|
|
198
|
+
regex: import("@angular/core").InputSignal<RegExp>;
|
|
199
|
+
validate(control: AbstractControl): ValidationErrors | null;
|
|
200
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<RegexValidatorDirective, never>;
|
|
201
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<RegexValidatorDirective, "[regexValidator]", never, {
|
|
202
|
+
"regex": {
|
|
203
|
+
"alias": "regex";
|
|
204
|
+
"required": false;
|
|
205
|
+
"isSignal": true;
|
|
206
|
+
};
|
|
207
|
+
}, {}, never, never, true, never>;
|
|
208
|
+
}
|
|
213
209
|
/**
|
|
214
210
|
* Shared types for the FAQ help system.
|
|
215
211
|
*
|
|
216
212
|
* Manifest is generated by the build-time validator (E9) and read at runtime
|
|
217
213
|
* by FaqContentLoader (E1) and FaqService (E2).
|
|
218
214
|
*/
|
|
219
|
-
type FaqAudience = 'public' | 'authenticated';
|
|
220
|
-
type FaqRenderContext = 'modal' | 'page';
|
|
215
|
+
export type FaqAudience = 'public' | 'authenticated';
|
|
216
|
+
export type FaqRenderContext = 'modal' | 'page';
|
|
221
217
|
/** Raw frontmatter as authored in the `.md` file. */
|
|
222
|
-
interface FaqFrontmatter {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
218
|
+
export interface FaqFrontmatter {
|
|
219
|
+
title: string;
|
|
220
|
+
category: string;
|
|
221
|
+
order?: number;
|
|
222
|
+
audience?: FaqAudience;
|
|
223
|
+
'image-prefix'?: string;
|
|
224
|
+
description?: string;
|
|
225
|
+
'seo-ready'?: boolean;
|
|
230
226
|
}
|
|
231
227
|
/** One row in the generated manifest — normalized form of frontmatter + file path. */
|
|
232
|
-
interface FaqEntry {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
interface FaqManifest {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
interface ParsedFaq {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
interface FaqRenderOptions {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
|
|
228
|
+
export interface FaqEntry {
|
|
229
|
+
/** Dot-notation key, e.g. "trade.segments". */
|
|
230
|
+
key: string;
|
|
231
|
+
title: string;
|
|
232
|
+
category: string;
|
|
233
|
+
order: number;
|
|
234
|
+
audience: FaqAudience;
|
|
235
|
+
description?: string;
|
|
236
|
+
seoReady: boolean;
|
|
237
|
+
imagePrefix?: string;
|
|
238
|
+
/**
|
|
239
|
+
* Path to the source file relative to `/assets/faq/`, without `.md`.
|
|
240
|
+
* Example: `trade/segments` (subdirs allowed; key path-separator is `.`,
|
|
241
|
+
* file path-separator is `/`).
|
|
242
|
+
*/
|
|
243
|
+
file: string;
|
|
244
|
+
}
|
|
245
|
+
export interface FaqManifest {
|
|
246
|
+
/** ISO timestamp set by the validator at generation time. */
|
|
247
|
+
generatedAt: string;
|
|
248
|
+
entries: FaqEntry[];
|
|
249
|
+
}
|
|
250
|
+
export interface ParsedFaq {
|
|
251
|
+
frontmatter: FaqFrontmatter;
|
|
252
|
+
body: string;
|
|
253
|
+
}
|
|
254
|
+
export interface FaqRenderOptions {
|
|
255
|
+
context: FaqRenderContext;
|
|
256
|
+
/**
|
|
257
|
+
* Optional override for the entry's `image-prefix` frontmatter.
|
|
258
|
+
* When set, relative image hrefs are prefixed with this string.
|
|
259
|
+
*/
|
|
260
|
+
imagePrefix?: string;
|
|
261
|
+
}
|
|
267
262
|
/**
|
|
268
263
|
* Pluggable content source for FAQ markdown + manifest.
|
|
269
264
|
*
|
|
@@ -272,17 +267,16 @@ interface FaqRenderOptions {
|
|
|
272
267
|
* may override (e.g. broken-stock-admin pulling from an internal source),
|
|
273
268
|
* but most apps will just use the HTTP default.
|
|
274
269
|
*/
|
|
275
|
-
interface FaqContentLoader {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
284
|
-
declare const FAQ_CONTENT_LOADER: InjectionToken<FaqContentLoader>;
|
|
285
|
-
|
|
270
|
+
export interface FaqContentLoader {
|
|
271
|
+
/** Loads (and may cache) the manifest. Idempotent. */
|
|
272
|
+
loadManifest(): Promise<FaqManifest>;
|
|
273
|
+
/**
|
|
274
|
+
* Resolves the manifest entry for `key`, then loads its .md body.
|
|
275
|
+
* Returns `null` when the key is not in the manifest.
|
|
276
|
+
*/
|
|
277
|
+
loadContent(key: string): Promise<string | null>;
|
|
278
|
+
}
|
|
279
|
+
export declare const FAQ_CONTENT_LOADER: InjectionToken<FaqContentLoader>;
|
|
286
280
|
/**
|
|
287
281
|
* Stateless markdown renderer for FAQ content.
|
|
288
282
|
*
|
|
@@ -299,14 +293,13 @@ declare const FAQ_CONTENT_LOADER: InjectionToken<FaqContentLoader>;
|
|
|
299
293
|
* In page context, links are emitted as plain navigable anchors; the prerendered
|
|
300
294
|
* static HTML can be clicked through directly.
|
|
301
295
|
*/
|
|
302
|
-
declare class FaqMarkdownRendererService {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
309
|
-
|
|
296
|
+
export declare class FaqMarkdownRendererService {
|
|
297
|
+
parse(raw: string): ParsedFaq;
|
|
298
|
+
render(body: string, opts: FaqRenderOptions): string;
|
|
299
|
+
private buildRenderer;
|
|
300
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FaqMarkdownRendererService, never>;
|
|
301
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
302
|
+
}
|
|
310
303
|
/**
|
|
311
304
|
* Default FaqContentLoader. Reads the build-generated manifest from
|
|
312
305
|
* `/assets/faq/manifest.json` and lazy-fetches individual `.md` files
|
|
@@ -314,32 +307,31 @@ declare class FaqMarkdownRendererService {
|
|
|
314
307
|
*
|
|
315
308
|
* Manifest is cached after first load; .md fetches are cached per key.
|
|
316
309
|
*/
|
|
317
|
-
declare class HttpFaqContentLoader implements FaqContentLoader {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
attempted: string[];
|
|
310
|
+
export declare class HttpFaqContentLoader implements FaqContentLoader {
|
|
311
|
+
private readonly http;
|
|
312
|
+
private manifestPromise;
|
|
313
|
+
private readonly contentCache;
|
|
314
|
+
loadManifest(): Promise<FaqManifest>;
|
|
315
|
+
loadContent(key: string): Promise<string | null>;
|
|
316
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<HttpFaqContentLoader, never>;
|
|
317
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
318
|
+
}
|
|
319
|
+
export interface FaqRegistrationHandle {
|
|
320
|
+
/** Call from the component's destroy hook (or DestroyRef) to clear the registration. */
|
|
321
|
+
deregister(): void;
|
|
322
|
+
}
|
|
323
|
+
export interface FaqResolution {
|
|
324
|
+
/** The key that produced a content match, or `null` when nothing matched. */
|
|
325
|
+
key: string | null;
|
|
326
|
+
/** Raw .md content for `key`, or `null` when nothing matched. */
|
|
327
|
+
content: string | null;
|
|
328
|
+
/** The entry whose content was returned, or `null` when nothing matched. */
|
|
329
|
+
entry: FaqEntry | null;
|
|
330
|
+
/**
|
|
331
|
+
* The chain of keys actually attempted, in order
|
|
332
|
+
* (e.g. `["trade.segments", "trade", "index"]`).
|
|
333
|
+
*/
|
|
334
|
+
attempted: string[];
|
|
343
335
|
}
|
|
344
336
|
/**
|
|
345
337
|
* Central FAQ state + resolution service.
|
|
@@ -351,82 +343,81 @@ interface FaqResolution {
|
|
|
351
343
|
* - Resolve a dot-notation key against the manifest with parent fallback,
|
|
352
344
|
* audience filtering, and `index` as the terminal fallback.
|
|
353
345
|
*/
|
|
354
|
-
declare class FaqService {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
declare function buildFallbackChain(key: string | null): string[];
|
|
428
|
-
declare function audienceAllowed(audience: FaqAudience, isAuthenticated: boolean): boolean;
|
|
429
|
-
|
|
346
|
+
export declare class FaqService {
|
|
347
|
+
private readonly loader;
|
|
348
|
+
private readonly history;
|
|
349
|
+
private readonly auth;
|
|
350
|
+
private readonly router;
|
|
351
|
+
private readonly _currentFaqKey;
|
|
352
|
+
/** Component-set current key. Bare `faq` opens at this key. */
|
|
353
|
+
readonly currentFaqKey: import("@angular/core").Signal<string | null>;
|
|
354
|
+
private readonly _manifest;
|
|
355
|
+
readonly manifest: import("@angular/core").Signal<FaqManifest | null>;
|
|
356
|
+
readonly entries: import("@angular/core").Signal<FaqEntry[]>;
|
|
357
|
+
private readonly _modalOpen;
|
|
358
|
+
readonly modalOpen: import("@angular/core").Signal<boolean>;
|
|
359
|
+
private readonly _activeEntry;
|
|
360
|
+
readonly activeEntry: import("@angular/core").Signal<FaqEntry | null>;
|
|
361
|
+
private readonly _activeContent;
|
|
362
|
+
readonly activeContent: import("@angular/core").Signal<string | null>;
|
|
363
|
+
private readonly _activeAttempted;
|
|
364
|
+
readonly activeAttempted: import("@angular/core").Signal<string[]>;
|
|
365
|
+
private readonly _navigating;
|
|
366
|
+
readonly navigating: import("@angular/core").Signal<boolean>;
|
|
367
|
+
private readonly _initialSearchTerm;
|
|
368
|
+
readonly initialSearchTerm: import("@angular/core").Signal<string | null>;
|
|
369
|
+
/** Called by the search UI once it has consumed the pre-fill term. */
|
|
370
|
+
consumeInitialSearchTerm(): void;
|
|
371
|
+
readonly canGoBack: import("@angular/core").Signal<boolean>;
|
|
372
|
+
readonly canGoForward: import("@angular/core").Signal<boolean>;
|
|
373
|
+
private manifestPromise;
|
|
374
|
+
/**
|
|
375
|
+
* Triggers manifest load if not already loaded. Idempotent. Subsequent
|
|
376
|
+
* calls return the cached promise.
|
|
377
|
+
*/
|
|
378
|
+
ensureManifest(): Promise<FaqManifest>;
|
|
379
|
+
/**
|
|
380
|
+
* Walks the dot-notation fallback chain (`a.b.c` → `a.b` → `a` → `index`)
|
|
381
|
+
* and returns the first entry whose audience allows the caller and whose
|
|
382
|
+
* content loads. Audience filtering: unauthenticated callers only see
|
|
383
|
+
* `audience: public` entries.
|
|
384
|
+
*/
|
|
385
|
+
resolveKey(key: string | null, isAuthenticated: boolean): Promise<FaqResolution>;
|
|
386
|
+
/**
|
|
387
|
+
* Component registration. Sets `currentFaqKey`; on `deregister()` the
|
|
388
|
+
* key is cleared only if it still matches (so a sibling tab activating
|
|
389
|
+
* mid-teardown doesn't get its key clobbered).
|
|
390
|
+
*/
|
|
391
|
+
register(key: string): FaqRegistrationHandle;
|
|
392
|
+
/**
|
|
393
|
+
* E3 — Open the FAQ modal. If `initialKey` is omitted, falls back to
|
|
394
|
+
* `currentFaqKey` and then to `'index'`. Resolves the key, loads content,
|
|
395
|
+
* and starts an E8 history session so subsequent in-modal nav uses
|
|
396
|
+
* `history.pushState`.
|
|
397
|
+
*/
|
|
398
|
+
openModal(initialKey?: string, opts?: {
|
|
399
|
+
searchTerm?: string;
|
|
400
|
+
}): Promise<void>;
|
|
401
|
+
/**
|
|
402
|
+
* Programmatic in-modal nav. Pushes a new history entry and loads `key`
|
|
403
|
+
* into the active slot. Use this for click handlers on `faq:` links and
|
|
404
|
+
* for the TOC sidebar.
|
|
405
|
+
*/
|
|
406
|
+
navigateTo(key: string): Promise<void>;
|
|
407
|
+
back(): void;
|
|
408
|
+
forward(): void;
|
|
409
|
+
/**
|
|
410
|
+
* Close button / Esc / backdrop close. Unwinds the FAQ history entries
|
|
411
|
+
* so the underlying app route is left untouched.
|
|
412
|
+
*/
|
|
413
|
+
closeModal(): void;
|
|
414
|
+
private finalizeClose;
|
|
415
|
+
private loadIntoActive;
|
|
416
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FaqService, never>;
|
|
417
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
418
|
+
}
|
|
419
|
+
export declare function buildFallbackChain(key: string | null): string[];
|
|
420
|
+
export declare function audienceAllowed(audience: FaqAudience, isAuthenticated: boolean): boolean;
|
|
430
421
|
/**
|
|
431
422
|
* E8 — In-modal navigation history.
|
|
432
423
|
*
|
|
@@ -446,57 +437,56 @@ declare function audienceAllowed(audience: FaqAudience, isAuthenticated: boolean
|
|
|
446
437
|
* to unwind our entries; suppress in-flight popstates while unwinding;
|
|
447
438
|
* finalize close once we've seen the expected number of popstates.
|
|
448
439
|
*/
|
|
449
|
-
declare class FaqHistoryService {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
}
|
|
490
|
-
|
|
440
|
+
export declare class FaqHistoryService {
|
|
441
|
+
private readonly _stack;
|
|
442
|
+
private readonly _position;
|
|
443
|
+
readonly stack: import("@angular/core").Signal<string[]>;
|
|
444
|
+
readonly position: import("@angular/core").Signal<number>;
|
|
445
|
+
/** True when popstate is processing the unwind kicked off by closeAndUnwind(). */
|
|
446
|
+
private unwinding;
|
|
447
|
+
private unwindRemaining;
|
|
448
|
+
private onUnwindComplete;
|
|
449
|
+
private popstateListener;
|
|
450
|
+
private onInModalNav;
|
|
451
|
+
private onExitModal;
|
|
452
|
+
/**
|
|
453
|
+
* Begin a new FAQ history session. Pushes the initial state entry and
|
|
454
|
+
* starts listening for popstate. Callbacks fire for in-modal navs and
|
|
455
|
+
* for popping out of FAQ entirely.
|
|
456
|
+
*/
|
|
457
|
+
begin(initialKey: string, onNav: (key: string) => void, onExit: () => void): void;
|
|
458
|
+
/** Push a new key onto the stack. Truncates any forward history. */
|
|
459
|
+
navigate(key: string): void;
|
|
460
|
+
canGoBack(): boolean;
|
|
461
|
+
canGoForward(): boolean;
|
|
462
|
+
/** Programmatic back — triggers a real browser back so popstate handles UI. */
|
|
463
|
+
back(): void;
|
|
464
|
+
forward(): void;
|
|
465
|
+
/** Currently rendered key. */
|
|
466
|
+
currentKey(): string | null;
|
|
467
|
+
/**
|
|
468
|
+
* Close the modal cleanly by unwinding our FAQ history entries so the
|
|
469
|
+
* underlying app route is left exactly where it was before the modal opened.
|
|
470
|
+
* `onComplete` fires after the unwind settles.
|
|
471
|
+
*/
|
|
472
|
+
closeAndUnwind(onComplete: () => void): void;
|
|
473
|
+
private teardown;
|
|
474
|
+
private pushHistoryState;
|
|
475
|
+
private attachListener;
|
|
476
|
+
private detachListener;
|
|
477
|
+
private handlePopState;
|
|
478
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FaqHistoryService, never>;
|
|
479
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
480
|
+
}
|
|
491
481
|
interface TocGroup {
|
|
492
|
-
|
|
493
|
-
|
|
482
|
+
category: string;
|
|
483
|
+
entries: FaqEntry[];
|
|
494
484
|
}
|
|
495
485
|
interface SearchResult {
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
486
|
+
key: string;
|
|
487
|
+
title: string;
|
|
488
|
+
category: string;
|
|
489
|
+
snippet: string;
|
|
500
490
|
}
|
|
501
491
|
/**
|
|
502
492
|
* E3 — FAQ modal. Two-pane layout (TOC sidebar + content pane), 80% viewport.
|
|
@@ -509,53 +499,52 @@ interface SearchResult {
|
|
|
509
499
|
* returnUrl-aware `authGuard` funnels unauthenticated users through
|
|
510
500
|
* `/login?returnUrl=...`, and `Login` honors returnUrl on success.
|
|
511
501
|
*/
|
|
512
|
-
declare class FaqModal {
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
|
|
502
|
+
export declare class FaqModal {
|
|
503
|
+
readonly devMode: boolean;
|
|
504
|
+
private readonly faq;
|
|
505
|
+
private readonly renderer;
|
|
506
|
+
private readonly auth;
|
|
507
|
+
private readonly router;
|
|
508
|
+
private readonly loader;
|
|
509
|
+
readonly open: import("@angular/core").Signal<boolean>;
|
|
510
|
+
readonly navigating: import("@angular/core").Signal<boolean>;
|
|
511
|
+
readonly activeEntry: import("@angular/core").Signal<FaqEntry | null>;
|
|
512
|
+
readonly attempted: import("@angular/core").Signal<string[]>;
|
|
513
|
+
readonly canGoBack: import("@angular/core").Signal<boolean>;
|
|
514
|
+
readonly canGoForward: import("@angular/core").Signal<boolean>;
|
|
515
|
+
readonly sidebarCollapsed: import("@angular/core").WritableSignal<boolean>;
|
|
516
|
+
readonly searchMode: import("@angular/core").WritableSignal<boolean>;
|
|
517
|
+
readonly searchQuery: import("@angular/core").WritableSignal<string>;
|
|
518
|
+
readonly searchLoading: import("@angular/core").WritableSignal<boolean>;
|
|
519
|
+
readonly searchResults: import("@angular/core").WritableSignal<SearchResult[]>;
|
|
520
|
+
private fuse;
|
|
521
|
+
private fuseBuild;
|
|
522
|
+
readonly renderedHtml: import("@angular/core").Signal<string>;
|
|
523
|
+
readonly toc: import("@angular/core").Signal<TocGroup[]>;
|
|
524
|
+
contentPaneRef?: ElementRef<HTMLDivElement>;
|
|
525
|
+
constructor();
|
|
526
|
+
onEscape(): void;
|
|
527
|
+
close(): void;
|
|
528
|
+
onBackdropClick(): void;
|
|
529
|
+
stopPropagation(event: Event): void;
|
|
530
|
+
toggleSidebar(): void;
|
|
531
|
+
goBack(): void;
|
|
532
|
+
goForward(): void;
|
|
533
|
+
goIndex(): void;
|
|
534
|
+
toggleSearch(): void;
|
|
535
|
+
onSearchInput(event: Event): void;
|
|
536
|
+
onSearchResultClick(key: string): void;
|
|
537
|
+
onTocClick(key: string): void;
|
|
538
|
+
private ensureSearchIndex;
|
|
539
|
+
private runSearch;
|
|
540
|
+
/**
|
|
541
|
+
* Event-delegated click handler for the rendered content pane.
|
|
542
|
+
* Intercepts our sentinel anchors emitted by FaqMarkdownRendererService.
|
|
543
|
+
*/
|
|
544
|
+
onContentClick(event: MouseEvent): void;
|
|
545
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FaqModal, never>;
|
|
546
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FaqModal, "faq-modal", never, {}, {}, never, never, true, never>;
|
|
547
|
+
}
|
|
559
548
|
/**
|
|
560
549
|
* Gate for the dev-only FAQ authoring panel (E11).
|
|
561
550
|
*
|
|
@@ -567,21 +556,20 @@ declare class FaqModal {
|
|
|
567
556
|
* The panel is loaded via dynamic `import()` and lives in its own lazy chunk;
|
|
568
557
|
* builds where this resolves to `false` never request the chunk.
|
|
569
558
|
*/
|
|
570
|
-
declare const FAQ_DEV_MODE: InjectionToken<boolean>;
|
|
559
|
+
export declare const FAQ_DEV_MODE: InjectionToken<boolean>;
|
|
571
560
|
/**
|
|
572
561
|
* Optional absolute filesystem root of the consuming app's workspace (e.g.
|
|
573
562
|
* `/Users/foo/dev/broken-stock`). When set, the authoring panel emits
|
|
574
563
|
* `webstorm://open?file=<root>/<relative-path>` deeplinks pointing at the
|
|
575
564
|
* exact source file. When unset, the panel falls back to a relative path.
|
|
576
565
|
*/
|
|
577
|
-
declare const FAQ_DEV_WORKSPACE_ROOT: InjectionToken<string>;
|
|
566
|
+
export declare const FAQ_DEV_WORKSPACE_ROOT: InjectionToken<string>;
|
|
578
567
|
/**
|
|
579
568
|
* Editor URL scheme for the "Open in editor" deeplink. Defaults to WebStorm.
|
|
580
569
|
* Set to `'vscode'` (or any custom scheme handler) per consuming app.
|
|
581
570
|
*/
|
|
582
|
-
type FaqDevEditor = 'webstorm' | 'vscode';
|
|
583
|
-
declare const FAQ_DEV_EDITOR: InjectionToken<FaqDevEditor>;
|
|
584
|
-
|
|
571
|
+
export type FaqDevEditor = 'webstorm' | 'vscode';
|
|
572
|
+
export declare const FAQ_DEV_EDITOR: InjectionToken<FaqDevEditor>;
|
|
585
573
|
/**
|
|
586
574
|
* E11 — Dev-only FAQ authoring panel.
|
|
587
575
|
*
|
|
@@ -594,45 +582,65 @@ declare const FAQ_DEV_EDITOR: InjectionToken<FaqDevEditor>;
|
|
|
594
582
|
* The panel never imports `environment.*` directly — gating is the modal's
|
|
595
583
|
* responsibility (via the FAQ_DEV_MODE token + dynamic import).
|
|
596
584
|
*/
|
|
597
|
-
declare class FaqAuthoringPanel {
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
585
|
+
export declare class FaqAuthoringPanel {
|
|
586
|
+
private readonly faq;
|
|
587
|
+
private readonly workspaceRoot;
|
|
588
|
+
private readonly editor;
|
|
589
|
+
/** `'missing'` shows the full scaffold; `'existing'` shows the collapsed strip. */
|
|
590
|
+
readonly mode: import("@angular/core").InputSignal<"missing" | "existing">;
|
|
591
|
+
/**
|
|
592
|
+
* The dot-notation key actually being authored. For `missing` mode this is
|
|
593
|
+
* the deepest attempted key (chain[0]); for `existing` it's the resolved key.
|
|
594
|
+
*/
|
|
595
|
+
readonly key: import("@angular/core").InputSignal<string>;
|
|
596
|
+
/** Fallback chain that was attempted. For `missing` every entry is missing. */
|
|
597
|
+
readonly attempted: import("@angular/core").InputSignal<string[]>;
|
|
598
|
+
/** Resolved entry — only set in `existing` mode. */
|
|
599
|
+
readonly entry: import("@angular/core").InputSignal<FaqEntry | null>;
|
|
600
|
+
readonly expanded: import("@angular/core").WritableSignal<boolean>;
|
|
601
|
+
readonly showAllFiles: import("@angular/core").WritableSignal<boolean>;
|
|
602
|
+
readonly copyState: import("@angular/core").WritableSignal<string | null>;
|
|
603
|
+
readonly relPath: import("@angular/core").Signal<string>;
|
|
604
|
+
readonly absPath: import("@angular/core").Signal<string>;
|
|
605
|
+
readonly editorHref: import("@angular/core").Signal<string>;
|
|
606
|
+
readonly editorLabel: import("@angular/core").Signal<"Open in VS Code" | "Open in WebStorm">;
|
|
607
|
+
readonly scaffoldCmd: import("@angular/core").Signal<string>;
|
|
608
|
+
readonly frontmatterScaffold: import("@angular/core").Signal<string>;
|
|
609
|
+
readonly allEntries: import("@angular/core").Signal<FaqEntry[]>;
|
|
610
|
+
readonly missedChain: import("@angular/core").Signal<{
|
|
611
|
+
key: string;
|
|
612
|
+
present: boolean;
|
|
613
|
+
}[]>;
|
|
614
|
+
toggleExpanded(): void;
|
|
615
|
+
toggleAllFiles(): void;
|
|
616
|
+
copy(text: string, label: string): Promise<void>;
|
|
617
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<FaqAuthoringPanel, never>;
|
|
618
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FaqAuthoringPanel, "faq-authoring-panel", never, {
|
|
619
|
+
"mode": {
|
|
620
|
+
"alias": "mode";
|
|
621
|
+
"required": true;
|
|
622
|
+
"isSignal": true;
|
|
623
|
+
};
|
|
624
|
+
"key": {
|
|
625
|
+
"alias": "key";
|
|
626
|
+
"required": true;
|
|
627
|
+
"isSignal": true;
|
|
628
|
+
};
|
|
629
|
+
"attempted": {
|
|
630
|
+
"alias": "attempted";
|
|
631
|
+
"required": false;
|
|
632
|
+
"isSignal": true;
|
|
633
|
+
};
|
|
634
|
+
"entry": {
|
|
635
|
+
"alias": "entry";
|
|
636
|
+
"required": false;
|
|
637
|
+
"isSignal": true;
|
|
638
|
+
};
|
|
639
|
+
}, {}, never, never, true, never>;
|
|
640
|
+
}
|
|
633
641
|
interface CategoryGroup$1 {
|
|
634
|
-
|
|
635
|
-
|
|
642
|
+
category: string;
|
|
643
|
+
entries: FaqEntry[];
|
|
636
644
|
}
|
|
637
645
|
/**
|
|
638
646
|
* Public, SEO-targeted FAQ index. Prerendered at build time on the
|
|
@@ -644,23 +652,22 @@ interface CategoryGroup$1 {
|
|
|
644
652
|
* JSON-LD (E18): emits a schema.org CollectionPage referencing
|
|
645
653
|
* each child entry as an ItemList.
|
|
646
654
|
*/
|
|
647
|
-
declare class PublicFaqIndex implements OnInit {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
}
|
|
663
|
-
|
|
655
|
+
export declare class PublicFaqIndex implements OnInit {
|
|
656
|
+
private readonly loader;
|
|
657
|
+
private readonly title;
|
|
658
|
+
private readonly meta;
|
|
659
|
+
private readonly siteBase;
|
|
660
|
+
private readonly publisher;
|
|
661
|
+
private readonly _entries;
|
|
662
|
+
readonly loading: import("@angular/core").WritableSignal<boolean>;
|
|
663
|
+
readonly visibleEntries: import("@angular/core").Signal<FaqEntry[]>;
|
|
664
|
+
readonly groups: import("@angular/core").Signal<CategoryGroup$1[]>;
|
|
665
|
+
readonly jsonLd: import("@angular/core").Signal<string | null>;
|
|
666
|
+
ngOnInit(): Promise<void>;
|
|
667
|
+
private applyMeta;
|
|
668
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PublicFaqIndex, never>;
|
|
669
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<PublicFaqIndex, "public-faq-index", never, {}, {}, never, never, true, never>;
|
|
670
|
+
}
|
|
664
671
|
/**
|
|
665
672
|
* Public, SEO-targeted FAQ page. Prerendered at build time per
|
|
666
673
|
* qualifying manifest entry (e.g. /help/landing, /help/trade/segments).
|
|
@@ -675,31 +682,30 @@ declare class PublicFaqIndex implements OnInit {
|
|
|
675
682
|
* raw JSON has `<` escaped to `<` so a stray `</script>` inside
|
|
676
683
|
* data won't break the surrounding tag.
|
|
677
684
|
*/
|
|
678
|
-
declare class PublicFaqPage implements OnInit {
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
}
|
|
699
|
-
|
|
685
|
+
export declare class PublicFaqPage implements OnInit {
|
|
686
|
+
private readonly route;
|
|
687
|
+
private readonly loader;
|
|
688
|
+
private readonly renderer;
|
|
689
|
+
private readonly title;
|
|
690
|
+
private readonly meta;
|
|
691
|
+
private readonly siteBase;
|
|
692
|
+
private readonly publisher;
|
|
693
|
+
readonly entry: import("@angular/core").WritableSignal<FaqEntry | null>;
|
|
694
|
+
readonly body: import("@angular/core").WritableSignal<string>;
|
|
695
|
+
readonly loading: import("@angular/core").WritableSignal<boolean>;
|
|
696
|
+
readonly activeFile: import("@angular/core").Signal<string | undefined>;
|
|
697
|
+
readonly renderedHtml: import("@angular/core").Signal<string>;
|
|
698
|
+
readonly jsonLd: import("@angular/core").Signal<string | null>;
|
|
699
|
+
ngOnInit(): Promise<void>;
|
|
700
|
+
private applyMeta;
|
|
701
|
+
private applyNotFoundMeta;
|
|
702
|
+
private canonicalUrl;
|
|
703
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PublicFaqPage, never>;
|
|
704
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<PublicFaqPage, "public-faq-page", never, {}, {}, never, never, true, never>;
|
|
705
|
+
}
|
|
700
706
|
interface CategoryGroup {
|
|
701
|
-
|
|
702
|
-
|
|
707
|
+
category: string;
|
|
708
|
+
entries: FaqEntry[];
|
|
703
709
|
}
|
|
704
710
|
/**
|
|
705
711
|
* Shared TOC sidebar for the public /help pages. Lists every
|
|
@@ -710,17 +716,22 @@ interface CategoryGroup {
|
|
|
710
716
|
* because the index page (`/help`) and entry pages
|
|
711
717
|
* (`/help/<file>`) all share this sidebar.
|
|
712
718
|
*/
|
|
713
|
-
declare class PublicFaqToc implements OnInit {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
719
|
+
export declare class PublicFaqToc implements OnInit {
|
|
720
|
+
private readonly loader;
|
|
721
|
+
/** Currently-active entry file path (e.g. `landing`). Empty/undefined on the index. */
|
|
722
|
+
readonly activeFile: import("@angular/core").InputSignal<string | undefined>;
|
|
723
|
+
private readonly _entries;
|
|
724
|
+
readonly groups: import("@angular/core").Signal<CategoryGroup[]>;
|
|
725
|
+
ngOnInit(): Promise<void>;
|
|
726
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PublicFaqToc, never>;
|
|
727
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<PublicFaqToc, "public-faq-toc", never, {
|
|
728
|
+
"activeFile": {
|
|
729
|
+
"alias": "activeFile";
|
|
730
|
+
"required": false;
|
|
731
|
+
"isSignal": true;
|
|
732
|
+
};
|
|
733
|
+
}, {}, never, never, true, never>;
|
|
734
|
+
}
|
|
724
735
|
/**
|
|
725
736
|
* Canonical public base URL (no trailing slash) used for OpenGraph,
|
|
726
737
|
* Twitter, JSON-LD canonical URLs, and sitemap entries on the
|
|
@@ -728,86 +739,81 @@ declare class PublicFaqToc implements OnInit {
|
|
|
728
739
|
* `og:url` etc. when unset, which is fine for previewing but
|
|
729
740
|
* suboptimal for production SEO. Consumer apps should override.
|
|
730
741
|
*/
|
|
731
|
-
declare const FAQ_PUBLIC_SITE_BASE_URL: InjectionToken<string>;
|
|
742
|
+
export declare const FAQ_PUBLIC_SITE_BASE_URL: InjectionToken<string>;
|
|
732
743
|
/**
|
|
733
744
|
* Publisher / author Organization embedded in schema.org JSON-LD on
|
|
734
745
|
* public FAQ pages. `logo` is optional — Google's validator warns but
|
|
735
746
|
* does not error when omitted. Add it later via a one-line provider
|
|
736
747
|
* override.
|
|
737
748
|
*/
|
|
738
|
-
interface FaqPublicPublisher {
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
}
|
|
743
|
-
declare const FAQ_PUBLIC_PUBLISHER: InjectionToken<FaqPublicPublisher>;
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
}
|
|
754
|
-
|
|
749
|
+
export interface FaqPublicPublisher {
|
|
750
|
+
name: string;
|
|
751
|
+
url?: string;
|
|
752
|
+
logo?: string;
|
|
753
|
+
}
|
|
754
|
+
export declare const FAQ_PUBLIC_PUBLISHER: InjectionToken<FaqPublicPublisher>;
|
|
755
|
+
export declare class ThinkingService {
|
|
756
|
+
#private;
|
|
757
|
+
readonly isThinking: import("@angular/core").Signal<boolean>;
|
|
758
|
+
startThinking(): void;
|
|
759
|
+
stopThinking(): void;
|
|
760
|
+
reset(): void;
|
|
761
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ThinkingService, never>;
|
|
762
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
763
|
+
}
|
|
755
764
|
/** Deliberately outside `bs.pref.` — see the header. */
|
|
756
|
-
declare const SESSION_TOKEN_KEY = "bs.auth.token";
|
|
757
|
-
declare class SessionTokenStore {
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
type RenewalDecision =
|
|
765
|
+
export declare const SESSION_TOKEN_KEY = "bs.auth.token";
|
|
766
|
+
export declare class SessionTokenStore {
|
|
767
|
+
#private;
|
|
768
|
+
get available(): boolean;
|
|
769
|
+
/**
|
|
770
|
+
* The stored token, or undefined.
|
|
771
|
+
*
|
|
772
|
+
* A stored value that is not a well-formed bearer is treated as absent and
|
|
773
|
+
* removed. It cannot be authenticated with, and leaving it there would make
|
|
774
|
+
* every later read fail the same way with nothing to explain it.
|
|
775
|
+
*/
|
|
776
|
+
read(): BearerToken | undefined;
|
|
777
|
+
/**
|
|
778
|
+
* Stores the token. THROWS on failure — see the header.
|
|
779
|
+
*
|
|
780
|
+
* @throws if the value is not a bearer token, or the write does not stick.
|
|
781
|
+
*/
|
|
782
|
+
write(token: BearerToken): void;
|
|
783
|
+
clear(): void;
|
|
784
|
+
/**
|
|
785
|
+
* Calls back when ANOTHER window changes the token.
|
|
786
|
+
*
|
|
787
|
+
* `storage` does not fire in the window that made the change, which is
|
|
788
|
+
* correct — that window is the one doing it and has already acted.
|
|
789
|
+
*
|
|
790
|
+
* ⚠ `event.key === null` is what `localStorage.clear()` produces, and it means
|
|
791
|
+
* everything went, this key included. Treating it as "not our key" is how a
|
|
792
|
+
* whole-storage clear silently fails to sign the user out.
|
|
793
|
+
*/
|
|
794
|
+
onExternalChange(handler: (token: BearerToken | undefined) => void): () => void;
|
|
795
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SessionTokenStore, never>;
|
|
796
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
797
|
+
}
|
|
798
|
+
export type RenewalDecision =
|
|
791
799
|
/** The token is gone: the window that renewed failed permanently, or signed out. */
|
|
792
|
-
'sign-out'
|
|
800
|
+
'sign-out' |
|
|
793
801
|
/** Another window renewed while we queued. Take its token; do NOT rotate again. */
|
|
794
|
-
|
|
802
|
+
'adopt' |
|
|
795
803
|
/** Nothing has moved. This window performs the renewal itself. */
|
|
796
|
-
|
|
804
|
+
'renew';
|
|
797
805
|
/**
|
|
798
806
|
* @param before the token this window held immediately BEFORE asking for the lock
|
|
799
807
|
* @param current the token in shared storage now that this window can act
|
|
800
808
|
*/
|
|
801
|
-
declare function decideRenewal(before: BearerToken | undefined, current: BearerToken | undefined): RenewalDecision;
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
}
|
|
810
|
-
|
|
809
|
+
export declare function decideRenewal(before: BearerToken | undefined, current: BearerToken | undefined): RenewalDecision;
|
|
810
|
+
export type ThemeMode = 'dark';
|
|
811
|
+
export declare class ThemeService {
|
|
812
|
+
readonly mode: import("@angular/core").WritableSignal<"dark">;
|
|
813
|
+
readonly isDark: import("@angular/core").Signal<boolean>;
|
|
814
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ThemeService, never>;
|
|
815
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
816
|
+
}
|
|
811
817
|
/**
|
|
812
818
|
* Asks the command box to take the cursor. Called by the shell after most
|
|
813
819
|
* actions (rail choices, Log Out, ?, ⧉, Escape, sign-in) and by apps.
|
|
@@ -819,14 +825,13 @@ declare class ThemeService {
|
|
|
819
825
|
* is refused. Refusing it at the source also means a request made on a phone is
|
|
820
826
|
* not left counted up, to be acted on after a rotation to a wider screen.
|
|
821
827
|
*/
|
|
822
|
-
declare class TerminalFocusService {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
}
|
|
829
|
-
|
|
828
|
+
export declare class TerminalFocusService {
|
|
829
|
+
#private;
|
|
830
|
+
readonly focusRequested: import("@angular/core").WritableSignal<number>;
|
|
831
|
+
requestFocus(): void;
|
|
832
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<TerminalFocusService, never>;
|
|
833
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
834
|
+
}
|
|
830
835
|
/**
|
|
831
836
|
* Controls visibility of the command terminal at the bottom of the shell.
|
|
832
837
|
*
|
|
@@ -868,21 +873,20 @@ declare class TerminalFocusService {
|
|
|
868
873
|
* a toggle while a screen has it hidden shows it again, and saves nothing, because the saved
|
|
869
874
|
* choice was "visible" all along.
|
|
870
875
|
*/
|
|
871
|
-
declare class TerminalVisibilityService {
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
}
|
|
885
|
-
|
|
876
|
+
export declare class TerminalVisibilityService {
|
|
877
|
+
#private;
|
|
878
|
+
/** Read-only on purpose — see the header. */
|
|
879
|
+
readonly visible: Signal<boolean>;
|
|
880
|
+
toggle(): void;
|
|
881
|
+
/**
|
|
882
|
+
* Hides the terminal while a screen needs the room, without changing what is saved. Returns the
|
|
883
|
+
* release; call it when the screen no longer needs it hidden (and on destroy). Releasing twice,
|
|
884
|
+
* or after ☰ already showed it, does nothing.
|
|
885
|
+
*/
|
|
886
|
+
hideForScreen(): () => void;
|
|
887
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<TerminalVisibilityService, never>;
|
|
888
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
889
|
+
}
|
|
886
890
|
/**
|
|
887
891
|
* Below this width the frame is in its phone layout: no icon column, a ☰ menu
|
|
888
892
|
* button in the top bar, a one-line top bar.
|
|
@@ -895,26 +899,25 @@ declare class TerminalVisibilityService {
|
|
|
895
899
|
* a media query cannot read a TypeScript constant. phone-layout.service.spec.ts
|
|
896
900
|
* fails if any of them drifts from this value.
|
|
897
901
|
*/
|
|
898
|
-
declare const SHELL_PHONE_MEDIA_QUERY = "(max-width: 819.98px)";
|
|
902
|
+
export declare const SHELL_PHONE_MEDIA_QUERY = "(max-width: 819.98px)";
|
|
899
903
|
/**
|
|
900
904
|
* Whether the frame is in its phone layout, and whether the phone menu is open.
|
|
901
905
|
*
|
|
902
906
|
* Desktop never sees any of this: `isPhone` is false there, the menu cannot open,
|
|
903
907
|
* and the rail renders exactly as before.
|
|
904
908
|
*/
|
|
905
|
-
declare class PhoneLayoutService {
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
}
|
|
917
|
-
|
|
909
|
+
export declare class PhoneLayoutService {
|
|
910
|
+
#private;
|
|
911
|
+
/** True below 820px. Always false where there is no window (server rendering). */
|
|
912
|
+
readonly isPhone: Signal<boolean>;
|
|
913
|
+
/** The ☰ menu's open state. Only ever true while `isPhone` is true. */
|
|
914
|
+
readonly menuOpen: Signal<boolean>;
|
|
915
|
+
constructor();
|
|
916
|
+
toggleMenu(): void;
|
|
917
|
+
closeMenu(): void;
|
|
918
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<PhoneLayoutService, never>;
|
|
919
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
920
|
+
}
|
|
918
921
|
/**
|
|
919
922
|
* A key binding a screen registers while it is mounted.
|
|
920
923
|
*
|
|
@@ -926,134 +929,131 @@ declare class PhoneLayoutService {
|
|
|
926
929
|
* work and then breaks on any non-US keyboard. `code: 'Digit1'` is the physical
|
|
927
930
|
* key and is layout-independent.
|
|
928
931
|
*/
|
|
929
|
-
interface ShortcutRegistration {
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
}
|
|
941
|
-
declare class KeyboardShortcutService {
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
}
|
|
966
|
-
|
|
932
|
+
export interface ShortcutRegistration {
|
|
933
|
+
/** Displayed in the help overlay, and matched against `event.key` unless `code` is set. */
|
|
934
|
+
key: string;
|
|
935
|
+
/**
|
|
936
|
+
* Match `event.code` instead — the PHYSICAL key, independent of layout and of
|
|
937
|
+
* what modifiers do to the character produced. Use for anything shifted.
|
|
938
|
+
*/
|
|
939
|
+
code?: string;
|
|
940
|
+
modifier?: 'Alt' | 'Shift';
|
|
941
|
+
action: () => void;
|
|
942
|
+
description: string;
|
|
943
|
+
}
|
|
944
|
+
export declare class KeyboardShortcutService {
|
|
945
|
+
#private;
|
|
946
|
+
readonly helpVisible: import("@angular/core").WritableSignal<boolean>;
|
|
947
|
+
/**
|
|
948
|
+
* True while unmodified shortcuts are being suppressed — the user is typing,
|
|
949
|
+
* or a dialog is open.
|
|
950
|
+
*
|
|
951
|
+
* Exposed because a screen binding BARE keys needs to tell the user whether the
|
|
952
|
+
* next keystroke acts or types, and that indicator must read the service's own
|
|
953
|
+
* dispatch state. A screen keeping its own boolean will eventually disagree
|
|
954
|
+
* with the service, and an indicator that lies about whether a keystroke sells
|
|
955
|
+
* a thousand shares is worse than no indicator at all.
|
|
956
|
+
*/
|
|
957
|
+
readonly suppressed: import("@angular/core").WritableSignal<boolean>;
|
|
958
|
+
readonly activeShortcuts: import("@angular/core").Signal<{
|
|
959
|
+
groupId: string;
|
|
960
|
+
shortcuts: ShortcutRegistration[];
|
|
961
|
+
} | null>;
|
|
962
|
+
constructor();
|
|
963
|
+
registerGroup(groupId: string, shortcuts: ShortcutRegistration[]): void;
|
|
964
|
+
unregisterGroup(groupId: string): void;
|
|
965
|
+
toggleHelp(): void;
|
|
966
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<KeyboardShortcutService, never>;
|
|
967
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
968
|
+
}
|
|
967
969
|
/** A key combination: the physical key and the modifiers that must be held, exactly. */
|
|
968
|
-
interface ScreenKeyCombo {
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
}
|
|
974
|
-
interface ScreenKeyRegistration {
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
970
|
+
export interface ScreenKeyCombo {
|
|
971
|
+
readonly code: string;
|
|
972
|
+
readonly ctrl: boolean;
|
|
973
|
+
readonly shift: boolean;
|
|
974
|
+
readonly alt: boolean;
|
|
975
|
+
}
|
|
976
|
+
export interface ScreenKeyRegistration {
|
|
977
|
+
/** The routes it is live on. */
|
|
978
|
+
readonly routes: readonly string[];
|
|
979
|
+
/** Called for each eligible keydown. Return true to take the key (the browser's action is prevented). */
|
|
980
|
+
handle(event: KeyboardEvent): boolean;
|
|
979
981
|
}
|
|
980
982
|
/** The class the command terminal's input carries; keys typed there still reach a screen's keys. */
|
|
981
|
-
declare const TERMINAL_INPUT_CLASS = "terminal-input";
|
|
983
|
+
export declare const TERMINAL_INPUT_CLASS = "terminal-input";
|
|
982
984
|
/** True when the key is typed into a box other than the command terminal. */
|
|
983
|
-
declare function isTypingElsewhere(target: EventTarget | null): boolean;
|
|
985
|
+
export declare function isTypingElsewhere(target: EventTarget | null): boolean;
|
|
984
986
|
/** Exactly this combination: the key, and these modifiers and no others. */
|
|
985
|
-
declare function comboMatches(combo: ScreenKeyCombo, e: Pick<KeyboardEvent, 'code' | 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'>): boolean;
|
|
987
|
+
export declare function comboMatches(combo: ScreenKeyCombo, e: Pick<KeyboardEvent, 'code' | 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'>): boolean;
|
|
986
988
|
/** Whether a keydown may reach a screen's keys at all. */
|
|
987
|
-
declare function isEligibleKey(e: Pick<KeyboardEvent, 'repeat' | 'isComposing' | 'target'>): boolean;
|
|
988
|
-
declare class ScreenKeysService {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
}
|
|
996
|
-
|
|
989
|
+
export declare function isEligibleKey(e: Pick<KeyboardEvent, 'repeat' | 'isComposing' | 'target'>): boolean;
|
|
990
|
+
export declare class ScreenKeysService {
|
|
991
|
+
#private;
|
|
992
|
+
constructor();
|
|
993
|
+
/** Registers keys for a screen; returns the function that removes them. */
|
|
994
|
+
register(registration: ScreenKeyRegistration): () => void;
|
|
995
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ScreenKeysService, never>;
|
|
996
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
997
|
+
}
|
|
997
998
|
/** The `reason` the auth worker puts on every authenticator refusal. */
|
|
998
|
-
type AuthenticatorReason = 'code-required' | 'invalid-code' | 'too-many-attempts' | 'authenticator-not-set-up' | 'setup-required' | 'passkey-refused';
|
|
999
|
+
export type AuthenticatorReason = 'code-required' | 'invalid-code' | 'too-many-attempts' | 'authenticator-not-set-up' | 'setup-required' | 'passkey-refused';
|
|
999
1000
|
/** Which extra step the sign-in screen is on, after the password was accepted. */
|
|
1000
|
-
type SignInCodeStep = 'none' | 'code' | 'setup';
|
|
1001
|
+
export type SignInCodeStep = 'none' | 'code' | 'setup';
|
|
1001
1002
|
/** GET {login}/authenticator. */
|
|
1002
|
-
type AuthenticatorStatus = {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1003
|
+
export type AuthenticatorStatus = {
|
|
1004
|
+
enrolled: boolean;
|
|
1005
|
+
recoveryCodesLeft: number;
|
|
1006
|
+
/** Yes/no only — the server never sends when it ends (D12). */
|
|
1007
|
+
bypassActive: boolean;
|
|
1008
|
+
codeRequired: boolean;
|
|
1008
1009
|
};
|
|
1009
1010
|
/** What a code-bearing request carries: a 6-digit code or a recovery code. */
|
|
1010
|
-
type CodeEntry = {
|
|
1011
|
-
|
|
1012
|
-
|
|
1011
|
+
export type CodeEntry = {
|
|
1012
|
+
code?: string;
|
|
1013
|
+
recoveryCode?: string;
|
|
1013
1014
|
};
|
|
1014
1015
|
/** The QR shown once during setup, and the same secret as text for typing in by hand. */
|
|
1015
|
-
type SetupChallenge = {
|
|
1016
|
-
|
|
1017
|
-
|
|
1016
|
+
export type SetupChallenge = {
|
|
1017
|
+
qr: string;
|
|
1018
|
+
key: string;
|
|
1018
1019
|
};
|
|
1019
1020
|
/**
|
|
1020
1021
|
* The one message for a pause (D8). The server sends no time and the screen invents none.
|
|
1021
1022
|
*/
|
|
1022
|
-
declare const TOO_MANY_ATTEMPTS = "Too many attempts.";
|
|
1023
|
+
export declare const TOO_MANY_ATTEMPTS = "Too many attempts.";
|
|
1023
1024
|
/** Plain words for each refusal, shown on the sign-in screen and in the code dialog. */
|
|
1024
|
-
declare function authenticatorMessage(reason: AuthenticatorReason | undefined): string;
|
|
1025
|
+
export declare function authenticatorMessage(reason: AuthenticatorReason | undefined): string;
|
|
1025
1026
|
/** The text file the Download button saves (D7). */
|
|
1026
|
-
declare function recoveryCodesFile(appName: string, username: string | undefined, codes: readonly string[], now?: Date): string;
|
|
1027
|
+
export declare function recoveryCodesFile(appName: string, username: string | undefined, codes: readonly string[], now?: Date): string;
|
|
1027
1028
|
/** One of the user's passkeys, as GET {login}/passkeys lists it (passkey-sign-in.prd.md D13, D17). */
|
|
1028
|
-
type PasskeySummary = {
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1029
|
+
export type PasskeySummary = {
|
|
1030
|
+
credentialId: string;
|
|
1031
|
+
name: string;
|
|
1032
|
+
addedAtEpoch: number;
|
|
1033
|
+
lastUsedAtEpoch: number | null;
|
|
1034
|
+
/** What the browser reported: synced between devices (D17). */
|
|
1035
|
+
backedUp: boolean;
|
|
1035
1036
|
};
|
|
1036
1037
|
/**
|
|
1037
1038
|
* What proves you are at the keyboard right now, for adding or removing a passkey (D14, D15):
|
|
1038
1039
|
* password + code (or recovery code), a code alone (removing only), or one of your passkeys.
|
|
1039
1040
|
*/
|
|
1040
|
-
type PasskeyProof = (CodeEntry & {
|
|
1041
|
-
|
|
1041
|
+
export type PasskeyProof = (CodeEntry & {
|
|
1042
|
+
password?: string;
|
|
1042
1043
|
}) | 'passkey';
|
|
1043
|
-
|
|
1044
1044
|
/**
|
|
1045
1045
|
* A refusal from an authenticator route, carrying the server's `reason`
|
|
1046
1046
|
* (authenticator-sign-in.prd.md). `message` is plain words for the screen.
|
|
1047
1047
|
*/
|
|
1048
|
-
declare class AuthenticatorError extends Error {
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1048
|
+
export declare class AuthenticatorError extends Error {
|
|
1049
|
+
readonly reason: AuthenticatorReason | undefined;
|
|
1050
|
+
readonly status: number;
|
|
1051
|
+
constructor(reason: AuthenticatorReason | undefined, message: string, status: number);
|
|
1052
1052
|
}
|
|
1053
1053
|
/** Sign-in rejected because the next step is a code (or setting one up), not because anything failed. */
|
|
1054
|
-
declare const CODE_STEP = "code-step";
|
|
1054
|
+
export declare const CODE_STEP = "code-step";
|
|
1055
1055
|
/** The user dismissed the passkey prompt — nothing failed, nothing to say. */
|
|
1056
|
-
declare const PASSKEY_CANCELLED = "passkey-cancelled";
|
|
1056
|
+
export declare const PASSKEY_CANCELLED = "passkey-cancelled";
|
|
1057
1057
|
/**
|
|
1058
1058
|
* What the user decided about sessions they already hold, sent on the SECOND
|
|
1059
1059
|
* sign-in attempt (multiple-devices D2/D6).
|
|
@@ -1062,14 +1062,14 @@ declare const PASSKEY_CANCELLED = "passkey-cancelled";
|
|
|
1062
1062
|
* over everything is `force` on the call itself, signing in beside them is
|
|
1063
1063
|
* `additional`, and ending a chosen few is `endSessionIds`.
|
|
1064
1064
|
*/
|
|
1065
|
-
type LoginChoice = {
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1065
|
+
export type LoginChoice = {
|
|
1066
|
+
additional?: boolean;
|
|
1067
|
+
/** Identifiers read off the refusal. The server acts on them only after the password verifies, and only for that user's own sessions. */
|
|
1068
|
+
endSessionIds?: string[];
|
|
1069
|
+
/** The 6-digit authenticator code (authenticator-sign-in.prd.md). */
|
|
1070
|
+
code?: string;
|
|
1071
|
+
/** A one-time recovery code, in place of `code`. */
|
|
1072
|
+
recoveryCode?: string;
|
|
1073
1073
|
};
|
|
1074
1074
|
/**
|
|
1075
1075
|
* The sign-in request body, exactly as sent (multiple-devices E8, authenticator-sign-in.prd.md).
|
|
@@ -1079,593 +1079,606 @@ type LoginChoice = {
|
|
|
1079
1079
|
* meant. A code typed now wins; otherwise a ticket from a 409 that followed a
|
|
1080
1080
|
* correct code stands in for it (the server accepts a code only once, D20).
|
|
1081
1081
|
*/
|
|
1082
|
-
declare function buildLoginBody(credentials: Credentials, force: boolean, options: LoginChoice, signInTicket?: string): Record<string, unknown>;
|
|
1083
|
-
declare class AuthenticationService {
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
}
|
|
1211
|
-
|
|
1212
|
-
declare function passkeysSupported(): boolean;
|
|
1082
|
+
export declare function buildLoginBody(credentials: Credentials, force: boolean, options: LoginChoice, signInTicket?: string): Record<string, unknown>;
|
|
1083
|
+
export declare class AuthenticationService {
|
|
1084
|
+
#private;
|
|
1085
|
+
readonly authenticated: import("@angular/core").WritableSignal<boolean>;
|
|
1086
|
+
readonly authenticating: import("@angular/core").WritableSignal<boolean>;
|
|
1087
|
+
readonly userSession: import("@angular/core").WritableSignal<UserSession | undefined>;
|
|
1088
|
+
readonly conflictSessions: import("@angular/core").WritableSignal<SessionSummary[] | undefined>;
|
|
1089
|
+
/**
|
|
1090
|
+
* Whether this app lets you sign in ALONGSIDE the sessions listed in
|
|
1091
|
+
* `conflictSessions` (multiple-devices D3).
|
|
1092
|
+
*
|
|
1093
|
+
* ⚠ READ FROM THE SERVER'S REFUSAL, NEVER DECIDED HERE. This component is
|
|
1094
|
+
* shared by the trading app and both admin consoles, which have different
|
|
1095
|
+
* rules. Deciding it locally would put the policy in two places, and the one
|
|
1096
|
+
* a reader would find is not the one that enforces it — an admin screen would
|
|
1097
|
+
* happily offer a button the server refuses.
|
|
1098
|
+
*/
|
|
1099
|
+
readonly additionalSignInAllowed: import("@angular/core").WritableSignal<boolean>;
|
|
1100
|
+
readonly pendingCredentials: import("@angular/core").WritableSignal<Credentials | undefined>;
|
|
1101
|
+
/** The extra step the sign-in screen is on after the password was accepted. */
|
|
1102
|
+
readonly codeStep: import("@angular/core").WritableSignal<SignInCodeStep>;
|
|
1103
|
+
/** Inside an AWS-opened setup window: the QR and key to show (D9, D10). */
|
|
1104
|
+
readonly setupChallenge: import("@angular/core").WritableSignal<SetupChallenge | undefined>;
|
|
1105
|
+
/** New recovery codes to show ONCE, with a Download button (D7). Cleared when acknowledged. */
|
|
1106
|
+
readonly recoveryCodesToShow: import("@angular/core").WritableSignal<string[] | undefined>;
|
|
1107
|
+
/** Recovery codes left, from the last sign-in or status read. */
|
|
1108
|
+
readonly recoveryCodesLeft: import("@angular/core").WritableSignal<number | undefined>;
|
|
1109
|
+
/**
|
|
1110
|
+
* Whether the AWS-set bypass is on for this account (D12). Drives the banner on
|
|
1111
|
+
* every screen. Yes/no only: the server never says when it ends.
|
|
1112
|
+
*/
|
|
1113
|
+
readonly bypassActive: import("@angular/core").WritableSignal<boolean>;
|
|
1114
|
+
/**
|
|
1115
|
+
* The open "already signed in elsewhere" choice came from a passkey sign-in, so its retry goes
|
|
1116
|
+
* to the passkey route with the ticket, not to /login with a password (passkey-sign-in.prd.md D8).
|
|
1117
|
+
*/
|
|
1118
|
+
readonly pendingPasskey: import("@angular/core").WritableSignal<boolean>;
|
|
1119
|
+
constructor();
|
|
1120
|
+
/**
|
|
1121
|
+
* The bearer for outbound calls.
|
|
1122
|
+
*
|
|
1123
|
+
* READ-THROUGH, not a cached copy, whenever the session is shared across
|
|
1124
|
+
* windows (PRD D2/D10). A copy is only as fresh as the last `storage` event
|
|
1125
|
+
* delivered, and the consumer that matters most is outside the request path:
|
|
1126
|
+
* the market-tape socket re-presents this token on a sixty-second heartbeat,
|
|
1127
|
+
* and the server closes the socket with a code the client does NOT retry if it
|
|
1128
|
+
* is stale. A window that was frozen when the event fired would lose its live
|
|
1129
|
+
* prices permanently. Reading through removes the question of which window
|
|
1130
|
+
* holds which token.
|
|
1131
|
+
*/
|
|
1132
|
+
get auth(): BearerToken;
|
|
1133
|
+
refresh(): Promise<boolean>;
|
|
1134
|
+
clearConflict(): void;
|
|
1135
|
+
/** Back to the password form from the code or setup step. */
|
|
1136
|
+
cancelCodeStep(): void;
|
|
1137
|
+
/** The new recovery codes have been seen (and, we hope, downloaded). */
|
|
1138
|
+
acknowledgeRecoveryCodes(): void;
|
|
1139
|
+
/**
|
|
1140
|
+
* The session is gone and cannot be recovered — renewal failed permanently,
|
|
1141
|
+
* or another window signed out.
|
|
1142
|
+
*
|
|
1143
|
+
* ⚠ THIS EXISTS BECAUSE THERE ARE TWO SIGN-OUT PATHS AND ONLY ONE USED TO
|
|
1144
|
+
* CLEAR THE TOKEN. `logout()` went through `#clearSession()`; the 401
|
|
1145
|
+
* interceptor set the signals and navigated but left `#auth` alone. Harmless
|
|
1146
|
+
* while the token lived only in memory and died with the tab — but once it is
|
|
1147
|
+
* in storage, a dead token left behind means every later window marks itself
|
|
1148
|
+
* signed in, passes the guard, renders, 401s and bounces to the login screen.
|
|
1149
|
+
* The reload fix would have become "the app flashes, then throws me out",
|
|
1150
|
+
* which is harder to diagnose than the behaviour it replaced.
|
|
1151
|
+
*/
|
|
1152
|
+
sessionLost(): void;
|
|
1153
|
+
logout(): void;
|
|
1154
|
+
/**
|
|
1155
|
+
* Signs in, and — on a second attempt — says what to do about the sessions
|
|
1156
|
+
* already held (multiple-devices D2/D6).
|
|
1157
|
+
*
|
|
1158
|
+
* `force` and `additional` are different answers, not synonyms: `force` ends
|
|
1159
|
+
* every session in this app, `additional` leaves them all running.
|
|
1160
|
+
* `endSessionIds` ends the ones the user picked and leaves the rest. None of
|
|
1161
|
+
* the three appears on a first attempt — they exist because the server
|
|
1162
|
+
* refused once and said what was live.
|
|
1163
|
+
*/
|
|
1164
|
+
login(credentials: Credentials, force?: boolean, options?: LoginChoice): Promise<void>;
|
|
1165
|
+
/**
|
|
1166
|
+
* Sign in with a passkey: no username, password or code. `conditional` waits quietly for the user
|
|
1167
|
+
* to pick a passkey from the username box's suggestions (D9); abort `signal` when the screen goes.
|
|
1168
|
+
* Rejects with PASSKEY_CANCELLED when the user dismissed the prompt, 'conflict' on a 409 (retry
|
|
1169
|
+
* with `retryPasskeySignIn`), or a plain-words message.
|
|
1170
|
+
*/
|
|
1171
|
+
loginWithPasskey(opts?: {
|
|
1172
|
+
conditional?: boolean;
|
|
1173
|
+
signal?: AbortSignal;
|
|
1174
|
+
}): Promise<void>;
|
|
1175
|
+
/** The answer to "already signed in elsewhere" after a passkey sign-in: the ticket stands in for the passkey (D8). */
|
|
1176
|
+
retryPasskeySignIn(force: boolean, choice?: Pick<LoginChoice, 'additional' | 'endSessionIds'>): Promise<void>;
|
|
1177
|
+
/** This user's passkeys. */
|
|
1178
|
+
listPasskeys(): Promise<PasskeySummary[]>;
|
|
1179
|
+
/**
|
|
1180
|
+
* Add a passkey (D13, D14). `proof` is password + code (or recovery code), or 'passkey' to prove
|
|
1181
|
+
* with one already added. Opens the browser's passkey prompt; resolves with the new passkey.
|
|
1182
|
+
*/
|
|
1183
|
+
addPasskey(name: string, proof: PasskeyProof): Promise<PasskeySummary>;
|
|
1184
|
+
/** Remove one of your passkeys (D15), proved with a code, a recovery code, or a passkey. */
|
|
1185
|
+
removePasskey(credentialId: string, proof: PasskeyProof): Promise<boolean>;
|
|
1186
|
+
/**
|
|
1187
|
+
* The on-demand check with a passkey (D11) — the passkey twin of `verifyCode`. Opens the
|
|
1188
|
+
* browser's prompt (fingerprint or PIN, always) and, on success, the server records it on this
|
|
1189
|
+
* session exactly as a code would. `purpose` is a label for the log, e.g. 'arm-trading'.
|
|
1190
|
+
* Resolves with when it was recorded; rejects with an AuthenticatorError (cancelled, refused, or
|
|
1191
|
+
* no passkey on this account — reason 'passkey-refused').
|
|
1192
|
+
*/
|
|
1193
|
+
verifyWithPasskey(purpose?: string): Promise<number>;
|
|
1194
|
+
/** Whether an authenticator is set up, codes left, and whether the bypass is on. Refreshes the banner. */
|
|
1195
|
+
authenticatorStatus(): Promise<AuthenticatorStatus>;
|
|
1196
|
+
/** Begin setting up (or replacing) the authenticator: the password again, plus a current code when replacing (D5). */
|
|
1197
|
+
startAuthenticatorSetup(password: string, current?: CodeEntry): Promise<SetupChallenge>;
|
|
1198
|
+
/** Finish setup with the first code from the newly scanned app. Returns the 10 recovery codes, once. */
|
|
1199
|
+
confirmAuthenticatorSetup(code: string): Promise<string[]>;
|
|
1200
|
+
/** Ten new recovery codes; the old ones stop working (D7). Needs a current code. */
|
|
1201
|
+
reissueRecoveryCodes(code: string): Promise<string[]>;
|
|
1202
|
+
/**
|
|
1203
|
+
* The on-demand check (D15): proves the code again on this session and returns
|
|
1204
|
+
* when it was recorded. `purpose` is a short label for the log, e.g.
|
|
1205
|
+
* 'arm-live-trading' — it changes nothing. The bypass never satisfies this.
|
|
1206
|
+
*/
|
|
1207
|
+
verifyCode(entry: CodeEntry, purpose?: string): Promise<number>;
|
|
1208
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AuthenticationService, never>;
|
|
1209
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
1210
|
+
}
|
|
1211
|
+
export declare function passkeysSupported(): boolean;
|
|
1213
1212
|
/** Whether the browser can offer passkeys in the username box's suggestions (D9). */
|
|
1214
|
-
declare function passkeyAutofillSupported(): Promise<boolean>;
|
|
1215
|
-
|
|
1213
|
+
export declare function passkeyAutofillSupported(): Promise<boolean>;
|
|
1216
1214
|
/**
|
|
1217
1215
|
* What a typed word resolves to: the command it names, plus any words the
|
|
1218
1216
|
* alternate name carries with it (see {@link CommandAlias}).
|
|
1219
1217
|
*/
|
|
1220
|
-
interface ResolvedCommand {
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
}
|
|
1224
|
-
declare class CommandRegistryService {
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
}
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
cancel?(): void;
|
|
1218
|
+
export interface ResolvedCommand {
|
|
1219
|
+
readonly command: CommandDefinition;
|
|
1220
|
+
readonly prependArgs: readonly string[];
|
|
1221
|
+
}
|
|
1222
|
+
export declare class CommandRegistryService {
|
|
1223
|
+
#private;
|
|
1224
|
+
private readonly commands;
|
|
1225
|
+
private readonly aliases;
|
|
1226
|
+
constructor();
|
|
1227
|
+
/**
|
|
1228
|
+
* Register a command and claim its names.
|
|
1229
|
+
*
|
|
1230
|
+
* ⭐ THROWS on a name already taken — real name or alternate. It used to be a
|
|
1231
|
+
* bare `set`, which meant a clash silently overwrote whichever command
|
|
1232
|
+
* registered first, and the constructor's "built-ins first so an app cannot
|
|
1233
|
+
* shadow them" comment was not actually true of the map. A shadowed command
|
|
1234
|
+
* is indistinguishable from a broken one, and this is the map dispatch reads,
|
|
1235
|
+
* so the failure belongs at startup where it cannot be missed
|
|
1236
|
+
* (scanner-command-speed.prd.md D4).
|
|
1237
|
+
*/
|
|
1238
|
+
register(command: CommandDefinition): void;
|
|
1239
|
+
/**
|
|
1240
|
+
* Resolve a word the user typed to the command it names — real name or
|
|
1241
|
+
* alternate. `prependArgs` is empty except for an alternate that carries a
|
|
1242
|
+
* subcommand.
|
|
1243
|
+
*
|
|
1244
|
+
* Use this wherever a TYPED word is being looked up. {@link get} deliberately
|
|
1245
|
+
* stays blind to alternates so callers that mean "the command called X" keep
|
|
1246
|
+
* meaning exactly that.
|
|
1247
|
+
*/
|
|
1248
|
+
resolve(name: string): ResolvedCommand | undefined;
|
|
1249
|
+
get(name: string): CommandDefinition | undefined;
|
|
1250
|
+
getAll(): CommandDefinition[];
|
|
1251
|
+
/**
|
|
1252
|
+
* Returns command name suggestions for tab-completion.
|
|
1253
|
+
* Built-ins (clear, help) are included when authenticated.
|
|
1254
|
+
*/
|
|
1255
|
+
getSuggestions(prefix: string, isAuthenticated: boolean): string[];
|
|
1256
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<CommandRegistryService, never>;
|
|
1257
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
1258
|
+
}
|
|
1259
|
+
export interface PendingPrompt {
|
|
1260
|
+
/** The question, shown in the terminal and as the input's placeholder. */
|
|
1261
|
+
readonly question: string;
|
|
1262
|
+
/** Mask what is typed (a code), in the input and in the history. */
|
|
1263
|
+
readonly masked?: boolean;
|
|
1264
|
+
/**
|
|
1265
|
+
* The answer: the line typed, trimmed — `''` for a bare Enter. The command decides what counts
|
|
1266
|
+
* as yes. It may ask again by calling `ask` from inside.
|
|
1267
|
+
*/
|
|
1268
|
+
answer(text: string): CommandResult | Promise<CommandResult>;
|
|
1269
|
+
/** Esc, or a new question replacing this one. */
|
|
1270
|
+
cancel?(): void;
|
|
1274
1271
|
}
|
|
1275
1272
|
/** True for the answers that mean yes: a bare Enter, `y`, `yes`. */
|
|
1276
|
-
declare function isYes(text: string): boolean;
|
|
1273
|
+
export declare function isYes(text: string): boolean;
|
|
1277
1274
|
/** What the history shows for an answer: the text, a masked run, or "(Enter)". */
|
|
1278
|
-
declare function answerEcho(prompt: PendingPrompt, text: string): string;
|
|
1275
|
+
export declare function answerEcho(prompt: PendingPrompt, text: string): string;
|
|
1279
1276
|
/** Holds at most one open question. Asking again cancels the one before it. */
|
|
1280
|
-
declare class PendingPromptState {
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1277
|
+
export declare class PendingPromptState {
|
|
1278
|
+
#private;
|
|
1279
|
+
get current(): PendingPrompt | null;
|
|
1280
|
+
ask(prompt: PendingPrompt): void;
|
|
1281
|
+
/**
|
|
1282
|
+
* Takes the open question and closes it, so an answer that asks again opens a fresh one rather
|
|
1283
|
+
* than being closed afterwards.
|
|
1284
|
+
*/
|
|
1285
|
+
take(): PendingPrompt | null;
|
|
1286
|
+
cancel(): boolean;
|
|
1287
|
+
}
|
|
1288
|
+
export declare class CommandService {
|
|
1289
|
+
#private;
|
|
1290
|
+
readonly history: import("@angular/core").WritableSignal<CommandHistoryEntry[]>;
|
|
1291
|
+
readonly currentInput: import("@angular/core").WritableSignal<string>;
|
|
1292
|
+
readonly isProcessing: import("@angular/core").WritableSignal<boolean>;
|
|
1293
|
+
readonly helpOverlay: import("@angular/core").WritableSignal<string | null>;
|
|
1294
|
+
/**
|
|
1295
|
+
* The question a command left open, or null (live-trading-foundation.prd.md D24). While set, the
|
|
1296
|
+
* next line — a bare Enter included — is its answer, not a command; Esc cancels.
|
|
1297
|
+
*/
|
|
1298
|
+
readonly pending: import("@angular/core").WritableSignal<PendingPrompt | null>;
|
|
1299
|
+
private readonly authService;
|
|
1300
|
+
private readonly registry;
|
|
1301
|
+
private readonly router;
|
|
1302
|
+
constructor();
|
|
1303
|
+
readonly lastResult: import("@angular/core").Signal<CommandResult | null>;
|
|
1304
|
+
dismissHelp(): void;
|
|
1305
|
+
/** Leaves a question open; the next line typed answers it. A question already open is cancelled. */
|
|
1306
|
+
ask(prompt: PendingPrompt): void;
|
|
1307
|
+
/** Esc: closes the open question, if any. True when there was one. */
|
|
1308
|
+
cancelPending(): boolean;
|
|
1309
|
+
echoCommand(input: string): void;
|
|
1310
|
+
pushNotification(notification: {
|
|
1311
|
+
message: string;
|
|
1312
|
+
severity: Severity;
|
|
1313
|
+
}): void;
|
|
1314
|
+
execute(rawInput: string): void;
|
|
1315
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<CommandService, never>;
|
|
1316
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<any>;
|
|
1317
|
+
}
|
|
1323
1318
|
/**
|
|
1324
1319
|
* Quote-aware command tokenizer. Treats "..." as single tokens and strips enclosing quotes.
|
|
1325
1320
|
*/
|
|
1326
|
-
declare function tokenize(input: string): string[];
|
|
1327
|
-
declare const CLEAR_USAGE: string;
|
|
1328
|
-
declare const HELP_USAGE: string;
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1321
|
+
export declare function tokenize(input: string): string[];
|
|
1322
|
+
export declare const CLEAR_USAGE: string;
|
|
1323
|
+
export declare const HELP_USAGE: string;
|
|
1324
|
+
export declare class Shell {
|
|
1325
|
+
#private;
|
|
1326
|
+
readonly isThinking: import("@angular/core").Signal<boolean>;
|
|
1327
|
+
readonly isAuthenticated: import("@angular/core").WritableSignal<boolean>;
|
|
1328
|
+
readonly helpOverlay: import("@angular/core").WritableSignal<string | null>;
|
|
1329
|
+
readonly terminalVisible: import("@angular/core").Signal<boolean>;
|
|
1330
|
+
/** Below 820px the icon column is replaced by the ☰ menu (scanner-phone-display D9). */
|
|
1331
|
+
readonly isPhone: import("@angular/core").Signal<boolean>;
|
|
1332
|
+
readonly phoneMenuOpen: import("@angular/core").Signal<boolean>;
|
|
1333
|
+
/**
|
|
1334
|
+
* Whether the command box takes the cursor when the console appears: on a
|
|
1335
|
+
* desktop yes, on a phone never (scanner-phone-display D10, Franz 2026-10-17).
|
|
1336
|
+
*
|
|
1337
|
+
* The console is shown on a phone again (reversing 0.8.1), but nothing may
|
|
1338
|
+
* put the cursor in its box there — the on-screen keyboard would cover the
|
|
1339
|
+
* screen. That includes the user turning the console ON from the ☰ menu: a
|
|
1340
|
+
* show is not a request to type, and the keyboard rising over the console the
|
|
1341
|
+
* moment it appears would hide the output the user wanted to see. Tapping the
|
|
1342
|
+
* box is the only way it gets the cursor on a phone.
|
|
1343
|
+
*
|
|
1344
|
+
* Rotating across 820px never mounts or unmounts the console (its visibility
|
|
1345
|
+
* is the user's choice alone), so this is only read when the user turns it on
|
|
1346
|
+
* or at start-up. CommandTerminal.focusInput also refuses on a phone, as a
|
|
1347
|
+
* second guard.
|
|
1348
|
+
*/
|
|
1349
|
+
readonly consoleFocusOnAppear: import("@angular/core").Signal<boolean>;
|
|
1350
|
+
/** "Sign-in code check is off." on every screen while the bypass is on (D12). */
|
|
1351
|
+
readonly bypassActive: import("@angular/core").WritableSignal<boolean>;
|
|
1352
|
+
closePhoneMenu(): void;
|
|
1353
|
+
dismissHelp(): void;
|
|
1354
|
+
readonly terminalHeight: import("@angular/core").WritableSignal<string>;
|
|
1355
|
+
readonly isDragging: import("@angular/core").WritableSignal<boolean>;
|
|
1356
|
+
onResizeStart(event: MouseEvent): void;
|
|
1357
|
+
onResizeTouchStart(event: TouchEvent): void;
|
|
1358
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<Shell, never>;
|
|
1359
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<Shell, "shell-root", never, {}, {}, never, ["*"], true, never>;
|
|
1360
|
+
}
|
|
1361
|
+
export declare class SideMenu {
|
|
1362
|
+
#private;
|
|
1363
|
+
readonly hasShortcuts: import("@angular/core").Signal<{
|
|
1364
|
+
groupId: string;
|
|
1365
|
+
shortcuts: import("@franzzemen/brokenstock-shell").ShortcutRegistration[];
|
|
1366
|
+
} | null>;
|
|
1367
|
+
readonly terminalVisible: import("@angular/core").Signal<boolean>;
|
|
1368
|
+
/**
|
|
1369
|
+
* Phone-menu rendering (scanner-phone-display D9): the same items as the icon
|
|
1370
|
+
* column, each with its label as visible text — a tooltip needs a hover a
|
|
1371
|
+
* touch screen does not have.
|
|
1372
|
+
*
|
|
1373
|
+
* ⚠ The keyboard-shortcuts button (Alt+?) is left out here. It opens a list of
|
|
1374
|
+
* Alt-key bindings, and a phone has no Alt key. The shortcuts themselves are
|
|
1375
|
+
* untouched — they are document-level listeners and work from a hardware
|
|
1376
|
+
* keyboard whatever the layout.
|
|
1377
|
+
*
|
|
1378
|
+
* The console show/hide button stays (Franz, 2026-10-17: the console is back
|
|
1379
|
+
* on phones, reversing 0.8.1). Turning the console ON from here does not put
|
|
1380
|
+
* the cursor in the command box on a phone — see CommandTerminal.focusOnAppear.
|
|
1381
|
+
*/
|
|
1382
|
+
readonly drawer: import("@angular/core").InputSignal<boolean>;
|
|
1383
|
+
readonly menuItems: import("@angular/core").Signal<MenuItem[]>;
|
|
1384
|
+
/**
|
|
1385
|
+
* Readiness, not entitlement — an unentitled item never reaches here, having
|
|
1386
|
+
* been filtered out by `visible` above. Read from the template so the
|
|
1387
|
+
* predicate's signal reads are tracked and the strip regreys live when the
|
|
1388
|
+
* user's state changes.
|
|
1389
|
+
*/
|
|
1390
|
+
isEnabled(item: MenuItem): boolean;
|
|
1391
|
+
disabledReasonFor(item: MenuItem): string;
|
|
1392
|
+
/** The item's attention badge, or null when there is nothing to draw (no badge, or a count of 0). */
|
|
1393
|
+
badgeFor(item: MenuItem): MenuBadge | null;
|
|
1394
|
+
onMenuClick(item: MenuItem): void;
|
|
1395
|
+
toggleHelp(): void;
|
|
1396
|
+
toggleTerminal(): void;
|
|
1397
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<SideMenu, never>;
|
|
1398
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<SideMenu, "shell-side-menu", never, {
|
|
1399
|
+
"drawer": {
|
|
1400
|
+
"alias": "drawer";
|
|
1401
|
+
"required": false;
|
|
1402
|
+
"isSignal": true;
|
|
1403
|
+
};
|
|
1404
|
+
}, {}, never, never, true, never>;
|
|
1405
|
+
}
|
|
1406
|
+
export declare class StatusBar {
|
|
1407
|
+
#private;
|
|
1408
|
+
readonly isAuthenticated: import("@angular/core").WritableSignal<boolean>;
|
|
1409
|
+
/** The ☰ button: phones only, and only when there is a menu to open (signed in). */
|
|
1410
|
+
readonly showMenuButton: import("@angular/core").Signal<boolean>;
|
|
1411
|
+
readonly menuOpen: import("@angular/core").Signal<boolean>;
|
|
1412
|
+
/**
|
|
1413
|
+
* Whether a second window is offered at all (multiple-windows PRD D14).
|
|
1414
|
+
*
|
|
1415
|
+
* ⚠ GATED ON SHARED SESSIONS, and this is a correctness gate rather than a
|
|
1416
|
+
* tidiness one. Without a session shared across windows, the new window starts
|
|
1417
|
+
* with no token, goes to the login screen, and signing in there ends the
|
|
1418
|
+
* session THIS window is using. A pop-out button in an app without sharing is
|
|
1419
|
+
* a button that signs you out — the exact failure the multiple-windows work
|
|
1420
|
+
* exists to remove.
|
|
1421
|
+
*/
|
|
1422
|
+
readonly canPopOut: import("@angular/core").Signal<boolean>;
|
|
1423
|
+
readonly loginStatus: import("@angular/core").Signal<"Authenticating..." | "Logged in" | "Not logged in">;
|
|
1424
|
+
logout(): void;
|
|
1425
|
+
/**
|
|
1426
|
+
* Open the screen you are on in a second, sized window.
|
|
1427
|
+
*
|
|
1428
|
+
* There is no mechanism by which this differs from the browser's own
|
|
1429
|
+
* "Duplicate tab": every window is a separate copy of the app started at some
|
|
1430
|
+
* address. It exists for the sizing and because it is discoverable — Ctrl+T,
|
|
1431
|
+
* a bookmark and dragging a tab out all work equally well once the session is
|
|
1432
|
+
* shared.
|
|
1433
|
+
*
|
|
1434
|
+
* `noopener` is deliberate. The new window needs nothing from this one — it
|
|
1435
|
+
* reads the session from storage — and leaving the opener reachable would give
|
|
1436
|
+
* two windows script access to each other for no purpose.
|
|
1437
|
+
*/
|
|
1438
|
+
popOut(): void;
|
|
1439
|
+
toggleMenu(): void;
|
|
1440
|
+
openFaq(): void;
|
|
1441
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<StatusBar, never>;
|
|
1442
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<StatusBar, "shell-status-bar", never, {}, {}, never, ["*"], true, never>;
|
|
1443
|
+
}
|
|
1444
|
+
export declare class MainContent {
|
|
1445
|
+
#private;
|
|
1446
|
+
readonly isThinking: import("@angular/core").Signal<boolean>;
|
|
1447
|
+
readonly shortcutHelpVisible: import("@angular/core").WritableSignal<boolean>;
|
|
1448
|
+
readonly activeShortcuts: import("@angular/core").Signal<{
|
|
1449
|
+
groupId: string;
|
|
1450
|
+
shortcuts: import("@franzzemen/brokenstock-shell").ShortcutRegistration[];
|
|
1451
|
+
} | null>;
|
|
1452
|
+
formatKey(key: string): string;
|
|
1453
|
+
closeShortcutHelp(): void;
|
|
1454
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<MainContent, never>;
|
|
1455
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<MainContent, "shell-main-content", never, {}, {}, never, never, true, never>;
|
|
1456
|
+
}
|
|
1457
|
+
export declare class CommandTerminal {
|
|
1458
|
+
#private;
|
|
1459
|
+
private readonly commandService;
|
|
1460
|
+
private readonly registry;
|
|
1461
|
+
private readonly authService;
|
|
1462
|
+
private readonly terminalFocusService;
|
|
1463
|
+
/**
|
|
1464
|
+
* Whether the command box takes the cursor when the console first appears.
|
|
1465
|
+
*
|
|
1466
|
+
* True for a standalone console. The shell passes false on a phone (below
|
|
1467
|
+
* 820px), including when the user turns the console on from the ☰ menu:
|
|
1468
|
+
* showing the console is not a request to type, and focusing the box would
|
|
1469
|
+
* raise the on-screen keyboard over it (scanner-phone-display D10). On a
|
|
1470
|
+
* phone, focusInput() refuses anyway; this keeps the intent explicit.
|
|
1471
|
+
*/
|
|
1472
|
+
readonly focusOnAppear: import("@angular/core").InputSignal<boolean>;
|
|
1473
|
+
readonly history: import("@angular/core").Signal<import("@franzzemen/brokenstock-shell").CommandHistoryEntry[]>;
|
|
1474
|
+
readonly currentInput: import("@angular/core").WritableSignal<string>;
|
|
1475
|
+
readonly isProcessing: import("@angular/core").WritableSignal<boolean>;
|
|
1476
|
+
/** The question a command left open (D24): the input answers it; Esc cancels. */
|
|
1477
|
+
readonly pending: import("@angular/core").WritableSignal<import("@franzzemen/brokenstock-shell").PendingPrompt | null>;
|
|
1478
|
+
private readonly inputEl;
|
|
1479
|
+
private tabCycleIndex;
|
|
1480
|
+
private lastTabPrefix;
|
|
1481
|
+
readonly ghostText: import("@angular/core").Signal<string>;
|
|
1482
|
+
constructor();
|
|
1483
|
+
onSubmit(): void;
|
|
1484
|
+
onInputChange(value: string): void;
|
|
1485
|
+
dismissHelp(): void;
|
|
1486
|
+
/** Esc: cancels an open question first, otherwise closes the help overlay. */
|
|
1487
|
+
onEscape(): void;
|
|
1488
|
+
onTab(event: KeyboardEvent): void;
|
|
1489
|
+
getSeverityClass(result: CommandResult): string;
|
|
1490
|
+
/**
|
|
1491
|
+
* ⚠ A no-op on a phone (scanner-phone-display D10/E11). The console is shown
|
|
1492
|
+
* there, but only the user tapping the box may give it the cursor; every
|
|
1493
|
+
* programmatic path (first appearance, a finished command, a focus request,
|
|
1494
|
+
* an app calling this) ends here and is refused, so nothing on a phone can
|
|
1495
|
+
* raise the on-screen keyboard unasked.
|
|
1496
|
+
*/
|
|
1497
|
+
focusInput(): void;
|
|
1498
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<CommandTerminal, never>;
|
|
1499
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<CommandTerminal, "shell-command-terminal", never, {
|
|
1500
|
+
"focusOnAppear": {
|
|
1501
|
+
"alias": "focusOnAppear";
|
|
1502
|
+
"required": false;
|
|
1503
|
+
"isSignal": true;
|
|
1504
|
+
};
|
|
1505
|
+
}, {}, never, never, true, never>;
|
|
1506
|
+
}
|
|
1507
|
+
export declare class Login implements OnInit, OnDestroy {
|
|
1508
|
+
#private;
|
|
1509
|
+
protected authenticationService: AuthenticationService;
|
|
1510
|
+
protected router: Router;
|
|
1511
|
+
model: Credentials;
|
|
1512
|
+
submitted: boolean;
|
|
1513
|
+
hide: import("@angular/core").WritableSignal<boolean>;
|
|
1514
|
+
errors: {
|
|
1515
|
+
ndx: number;
|
|
1516
|
+
message: string;
|
|
1517
|
+
}[];
|
|
1518
|
+
/**
|
|
1519
|
+
* Session identifiers the user has ticked for ending (multiple-devices D7).
|
|
1520
|
+
*
|
|
1521
|
+
* ⚠ THE TAKEOVER CHECKBOX THAT USED TO LIVE ON THIS FORM IS GONE, and its
|
|
1522
|
+
* removal is the point rather than a tidy-up. It defaulted to ON and sat on
|
|
1523
|
+
* the FIRST form, before anything was known about what was live — so once a
|
|
1524
|
+
* user deliberately holds sessions on two machines, one absent-minded submit
|
|
1525
|
+
* ended the session on the machine in the other room. The choice belongs
|
|
1526
|
+
* here, on the screen that lists what is actually running.
|
|
1527
|
+
*/
|
|
1528
|
+
readonly selected: import("@angular/core").WritableSignal<ReadonlySet<string>>;
|
|
1529
|
+
/** The code or recovery code typed on the code step (authenticator-sign-in.prd.md). */
|
|
1530
|
+
codeText: string;
|
|
1531
|
+
/** Typing a recovery code instead of the 6-digit code. */
|
|
1532
|
+
readonly useRecovery: import("@angular/core").WritableSignal<boolean>;
|
|
1533
|
+
protected readonly USERNAME_REGEX: RegExp;
|
|
1534
|
+
protected readonly PASSWORD_REGEX: RegExp;
|
|
1535
|
+
protected readonly appName: string;
|
|
1536
|
+
/** Whether this browser can sign in with a passkey at all; the button is hidden otherwise (passkey D2). */
|
|
1537
|
+
protected readonly passkeys: boolean;
|
|
1538
|
+
/**
|
|
1539
|
+
* Shown when the passkey prompt closed without a passkey: most often this device has none (a
|
|
1540
|
+
* passkey lives on the device or account that made it). Plain advice, not an error.
|
|
1541
|
+
*/
|
|
1542
|
+
protected readonly passkeyHint: import("@angular/core").WritableSignal<boolean>;
|
|
1543
|
+
constructor(authenticationService: AuthenticationService, router: Router);
|
|
1544
|
+
/** Safe on the server: the support check is false wherever there is no browser. */
|
|
1545
|
+
ngOnInit(): void;
|
|
1546
|
+
ngOnDestroy(): void;
|
|
1547
|
+
/** "Sign in with a passkey". The quiet wait must stop first: a browser runs one passkey prompt at a time. */
|
|
1548
|
+
onPasskey(): Promise<void>;
|
|
1549
|
+
onAutofill(event: AutofillEvent, inputEl: HTMLInputElement): void;
|
|
1550
|
+
clickEvent(event: MouseEvent): void;
|
|
1551
|
+
onSubmit(event: Event): void;
|
|
1552
|
+
/** Whether individual sessions can be ticked: only when the server named them. */
|
|
1553
|
+
canEndIndividually(): boolean;
|
|
1554
|
+
isSelected(session: SessionSummary): boolean;
|
|
1555
|
+
toggleSelected(session: SessionSummary, checked: boolean): void;
|
|
1556
|
+
selectedCount(): number;
|
|
1557
|
+
/**
|
|
1558
|
+
* What to call a session on screen.
|
|
1559
|
+
*
|
|
1560
|
+
* Falls back to the application name, which is what this screen showed before
|
|
1561
|
+
* devices were recorded, and then to something plainly non-committal. It never
|
|
1562
|
+
* invents a device: a session that predates the column genuinely has none, and
|
|
1563
|
+
* guessing would make two machines look like one.
|
|
1564
|
+
*/
|
|
1565
|
+
deviceLabel(session: SessionSummary): string;
|
|
1566
|
+
onTakeOver(): void;
|
|
1567
|
+
onSignInAsWell(): void;
|
|
1568
|
+
onEndSelected(): void;
|
|
1569
|
+
/** The code step: the 6-digit code (or a recovery code), with the credentials already accepted. */
|
|
1570
|
+
onSubmitCode(event: Event): void;
|
|
1571
|
+
onToggleRecovery(): void;
|
|
1572
|
+
/** Back to the password form. */
|
|
1573
|
+
onCancelCode(): void;
|
|
1574
|
+
/** New recovery codes were shown after sign-in; now go where sign-in was going. */
|
|
1575
|
+
onRecoveryCodesSaved(): void;
|
|
1576
|
+
onCancelConflict(): void;
|
|
1577
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<Login, never>;
|
|
1578
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<Login, "shell-login", never, {}, {}, never, never, true, never>;
|
|
1579
|
+
}
|
|
1580
|
+
export declare class RecoveryCodes {
|
|
1581
|
+
readonly codes: import("@angular/core").InputSignal<readonly string[]>;
|
|
1582
|
+
readonly appName: import("@angular/core").InputSignal<string>;
|
|
1583
|
+
readonly username: import("@angular/core").InputSignal<string | undefined>;
|
|
1584
|
+
readonly done: import("@angular/core").OutputEmitterRef<void>;
|
|
1585
|
+
protected readonly copied: import("@angular/core").WritableSignal<boolean>;
|
|
1586
|
+
download(): void;
|
|
1587
|
+
copy(): void;
|
|
1588
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<RecoveryCodes, never>;
|
|
1589
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<RecoveryCodes, "shell-recovery-codes", never, {
|
|
1590
|
+
"codes": {
|
|
1591
|
+
"alias": "codes";
|
|
1592
|
+
"required": true;
|
|
1593
|
+
"isSignal": true;
|
|
1594
|
+
};
|
|
1595
|
+
"appName": {
|
|
1596
|
+
"alias": "appName";
|
|
1597
|
+
"required": false;
|
|
1598
|
+
"isSignal": true;
|
|
1599
|
+
};
|
|
1600
|
+
"username": {
|
|
1601
|
+
"alias": "username";
|
|
1602
|
+
"required": false;
|
|
1603
|
+
"isSignal": true;
|
|
1604
|
+
};
|
|
1605
|
+
}, {
|
|
1606
|
+
"done": "done";
|
|
1607
|
+
}, never, never, true, never>;
|
|
1608
|
+
}
|
|
1592
1609
|
type Mode = 'view' | 'password' | 'scan' | 'reissue' | 'codes' | 'passkey-add' | 'passkey-remove';
|
|
1593
|
-
declare class AuthenticatorSettings implements OnInit {
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<CodeDialog, "shell-code-dialog", never, {}, {}, never, never, true, never>;
|
|
1610
|
+
export declare class AuthenticatorSettings implements OnInit {
|
|
1611
|
+
#private;
|
|
1612
|
+
protected readonly appName: string;
|
|
1613
|
+
protected readonly status: import("@angular/core").WritableSignal<AuthenticatorStatus | undefined>;
|
|
1614
|
+
protected readonly mode: import("@angular/core").WritableSignal<Mode>;
|
|
1615
|
+
protected readonly busy: import("@angular/core").WritableSignal<boolean>;
|
|
1616
|
+
protected readonly error: import("@angular/core").WritableSignal<string | undefined>;
|
|
1617
|
+
protected readonly challenge: import("@angular/core").WritableSignal<SetupChallenge | undefined>;
|
|
1618
|
+
protected readonly codes: import("@angular/core").WritableSignal<string[] | undefined>;
|
|
1619
|
+
protected readonly useRecovery: import("@angular/core").WritableSignal<boolean>;
|
|
1620
|
+
/** Whether this browser can make and use passkeys at all. */
|
|
1621
|
+
protected readonly passkeys: boolean;
|
|
1622
|
+
protected readonly passkeyList: import("@angular/core").WritableSignal<PasskeySummary[] | undefined>;
|
|
1623
|
+
/** The passkey being removed, while its confirmation is on screen. */
|
|
1624
|
+
protected readonly removing: import("@angular/core").WritableSignal<PasskeySummary | undefined>;
|
|
1625
|
+
password: string;
|
|
1626
|
+
code: string;
|
|
1627
|
+
passkeyName: string;
|
|
1628
|
+
ngOnInit(): void;
|
|
1629
|
+
beginAddPasskey(): void;
|
|
1630
|
+
beginRemovePasskey(passkey: PasskeySummary): void;
|
|
1631
|
+
/** Add, proved with password + code (or recovery code) — or with a passkey already added. */
|
|
1632
|
+
addPasskey(event: Event | undefined, withPasskey?: boolean): Promise<void>;
|
|
1633
|
+
/** Remove, proved with a code or recovery code — or with a passkey. */
|
|
1634
|
+
removePasskey(event: Event | undefined, withPasskey?: boolean): Promise<void>;
|
|
1635
|
+
begin(mode: 'password' | 'reissue'): void;
|
|
1636
|
+
cancel(): void;
|
|
1637
|
+
toggleRecovery(): void;
|
|
1638
|
+
/** Password (and current code when replacing) → QR. */
|
|
1639
|
+
start(event: Event): Promise<void>;
|
|
1640
|
+
/** The first code from the newly scanned app → recovery codes. */
|
|
1641
|
+
confirm(event: Event): Promise<void>;
|
|
1642
|
+
reissue(event: Event): Promise<void>;
|
|
1643
|
+
codesSaved(): Promise<void>;
|
|
1644
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<AuthenticatorSettings, never>;
|
|
1645
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<AuthenticatorSettings, "shell-authenticator-settings", never, {}, {}, never, never, true, never>;
|
|
1646
|
+
}
|
|
1647
|
+
export interface CodeDialogData {
|
|
1648
|
+
title: string;
|
|
1649
|
+
/** One line under the title saying what the code is for. */
|
|
1650
|
+
message?: string;
|
|
1651
|
+
/** Short lowercase label for the log, e.g. 'arm-live-trading' (D17). Changes nothing. */
|
|
1652
|
+
purpose?: string;
|
|
1653
|
+
confirmLabel?: string;
|
|
1654
|
+
}
|
|
1655
|
+
export declare class CodeDialog {
|
|
1656
|
+
#private;
|
|
1657
|
+
readonly data: CodeDialogData;
|
|
1658
|
+
readonly dialogRef: MatDialogRef<any, any>;
|
|
1659
|
+
text: string;
|
|
1660
|
+
protected readonly useRecovery: import("@angular/core").WritableSignal<boolean>;
|
|
1661
|
+
protected readonly busy: import("@angular/core").WritableSignal<boolean>;
|
|
1662
|
+
protected readonly error: import("@angular/core").WritableSignal<string | undefined>;
|
|
1663
|
+
toggleRecovery(): void;
|
|
1664
|
+
submit(event: Event): Promise<void>;
|
|
1665
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<CodeDialog, never>;
|
|
1666
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<CodeDialog, "shell-code-dialog", never, {}, {}, never, never, true, never>;
|
|
1651
1667
|
}
|
|
1652
1668
|
/** Opens the dialog; resolves with the recorded time (epoch ms), or undefined when cancelled. */
|
|
1653
|
-
declare function openCodeDialog(dialog: MatDialog, data: CodeDialogData): Promise<number | undefined>;
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
}
|
|
1668
|
-
|
|
1669
|
-
export { AuthenticationService, AuthenticatorError, AuthenticatorSettings, CLEAR_USAGE, CODE_STEP, CodeDialog, CommandRegistryService, CommandService, CommandTerminal, ConfirmDialog, FAQ_CONTENT_LOADER, FAQ_DEV_EDITOR, FAQ_DEV_MODE, FAQ_DEV_WORKSPACE_ROOT, FAQ_PUBLIC_PUBLISHER, FAQ_PUBLIC_SITE_BASE_URL, FaqAuthoringPanel, FaqHistoryService, FaqMarkdownRendererService, FaqModal, FaqService, HELP_USAGE, HttpFaqContentLoader, KeyboardShortcutService, Login, MainContent, PASSKEY_CANCELLED, PendingPromptState, PhoneLayoutService, PublicFaqIndex, PublicFaqPage, PublicFaqToc, RecoveryCodes, RegexValidatorDirective, SESSION_TOKEN_KEY, SHELL_APP_NAME, SHELL_AUTH_URLS, SHELL_COMMANDS, SHELL_MENU_ITEMS, SHELL_PHONE_MEDIA_QUERY, SHELL_POST_LOGIN_ROUTE, SHELL_SHARED_SESSION, ScreenKeysService, SessionTokenStore, Shell, SideMenu, StatusBar, TERMINAL_INPUT_CLASS, TOO_MANY_ATTEMPTS, TerminalFocusService, TerminalVisibilityService, ThemeService, ThinkingService, answerEcho, audienceAllowed, authGuard, authInterceptor, authenticatorMessage, buildFallbackChain, buildLoginBody, comboMatches, decideRenewal, guestGuard, isEligibleKey, isTypingElsewhere, isYes, matchesRoute, openCodeDialog, passkeyAutofillSupported, passkeysSupported, provideShellConfig, recoveryCodesFile, tokenize };
|
|
1670
|
-
export type { AuthenticatorReason, AuthenticatorStatus, CodeDialogData, CodeEntry, CommandAlias, CommandAvailability, CommandDefinition, CommandHistoryEntry, CommandLexiconEntry, CommandResult, CommandScope, ConfirmDialogData, FaqAudience, FaqContentLoader, FaqDevEditor, FaqEntry, FaqFrontmatter, FaqManifest, FaqPublicPublisher, FaqRegistrationHandle, FaqRenderContext, FaqRenderOptions, FaqResolution, LoginChoice, MenuBadge, MenuItem, ParsedFaq, PasskeyProof, PasskeySummary, PendingPrompt, RenewalDecision, ResolvedCommand, RouteScope, ScreenKeyCombo, ScreenKeyRegistration, SetupChallenge, Severity, ShellAuthUrls, ShellConfig, ShortcutRegistration, SignInCodeStep, ThemeMode };
|
|
1671
|
-
//# sourceMappingURL=franzzemen-brokenstock-shell.d.ts.map
|
|
1669
|
+
export declare function openCodeDialog(dialog: MatDialog, data: CodeDialogData): Promise<number | undefined>;
|
|
1670
|
+
export interface ConfirmDialogData {
|
|
1671
|
+
title: string;
|
|
1672
|
+
message: string;
|
|
1673
|
+
warningMessage?: string;
|
|
1674
|
+
confirmLabel?: string;
|
|
1675
|
+
cancelLabel?: string;
|
|
1676
|
+
}
|
|
1677
|
+
export declare class ConfirmDialog {
|
|
1678
|
+
readonly data: ConfirmDialogData;
|
|
1679
|
+
readonly dialogRef: MatDialogRef<any, any>;
|
|
1680
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ConfirmDialog, never>;
|
|
1681
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ConfirmDialog, "shell-confirm-dialog", never, {}, {}, never, never, true, never>;
|
|
1682
|
+
}
|
|
1683
|
+
export {};
|
|
1684
|
+
//# sourceMappingURL=franzzemen-brokenstock-shell.d.ts.map
|