@franzzemen/brokenstock-shell 0.10.2 → 0.10.4
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
|
-
severity?: Severity;
|
|
19
|
-
isHelp?: boolean;
|
|
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;
|
|
20
18
|
}
|
|
21
|
-
interface CommandHistoryEntry {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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,74 +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
|
-
|
|
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>;
|
|
156
164
|
/**
|
|
157
165
|
* App-specific commands to register alongside the built-in command set.
|
|
158
166
|
* Provide via provideShellConfig().
|
|
159
167
|
*/
|
|
160
|
-
declare const SHELL_COMMANDS: InjectionToken<CommandDefinition[]>;
|
|
161
|
-
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>;
|
|
162
170
|
/**
|
|
163
171
|
* Whether this app shares one session across every window of the browser
|
|
164
172
|
* (multiple-windows PRD D2/D7).
|
|
@@ -170,88 +178,87 @@ declare const SHELL_POST_LOGIN_ROUTE: InjectionToken<string>;
|
|
|
170
178
|
* different exposure — extending this to them is a separate decision, not a
|
|
171
179
|
* default.
|
|
172
180
|
*/
|
|
173
|
-
declare const SHELL_SHARED_SESSION: InjectionToken<boolean>;
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
declare
|
|
187
|
-
|
|
188
|
-
declare const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
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
|
+
}
|
|
201
209
|
/**
|
|
202
210
|
* Shared types for the FAQ help system.
|
|
203
211
|
*
|
|
204
212
|
* Manifest is generated by the build-time validator (E9) and read at runtime
|
|
205
213
|
* by FaqContentLoader (E1) and FaqService (E2).
|
|
206
214
|
*/
|
|
207
|
-
type FaqAudience = 'public' | 'authenticated';
|
|
208
|
-
type FaqRenderContext = 'modal' | 'page';
|
|
215
|
+
export type FaqAudience = 'public' | 'authenticated';
|
|
216
|
+
export type FaqRenderContext = 'modal' | 'page';
|
|
209
217
|
/** Raw frontmatter as authored in the `.md` file. */
|
|
210
|
-
interface FaqFrontmatter {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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;
|
|
218
226
|
}
|
|
219
227
|
/** One row in the generated manifest — normalized form of frontmatter + file path. */
|
|
220
|
-
interface FaqEntry {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
237
|
-
interface FaqManifest {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
242
|
-
interface ParsedFaq {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
interface FaqRenderOptions {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
|
|
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
|
+
}
|
|
255
262
|
/**
|
|
256
263
|
* Pluggable content source for FAQ markdown + manifest.
|
|
257
264
|
*
|
|
@@ -260,17 +267,16 @@ interface FaqRenderOptions {
|
|
|
260
267
|
* may override (e.g. broken-stock-admin pulling from an internal source),
|
|
261
268
|
* but most apps will just use the HTTP default.
|
|
262
269
|
*/
|
|
263
|
-
interface FaqContentLoader {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
272
|
-
declare const FAQ_CONTENT_LOADER: InjectionToken<FaqContentLoader>;
|
|
273
|
-
|
|
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>;
|
|
274
280
|
/**
|
|
275
281
|
* Stateless markdown renderer for FAQ content.
|
|
276
282
|
*
|
|
@@ -287,14 +293,13 @@ declare const FAQ_CONTENT_LOADER: InjectionToken<FaqContentLoader>;
|
|
|
287
293
|
* In page context, links are emitted as plain navigable anchors; the prerendered
|
|
288
294
|
* static HTML can be clicked through directly.
|
|
289
295
|
*/
|
|
290
|
-
declare class FaqMarkdownRendererService {
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
}
|
|
297
|
-
|
|
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
|
+
}
|
|
298
303
|
/**
|
|
299
304
|
* Default FaqContentLoader. Reads the build-generated manifest from
|
|
300
305
|
* `/assets/faq/manifest.json` and lazy-fetches individual `.md` files
|
|
@@ -302,32 +307,31 @@ declare class FaqMarkdownRendererService {
|
|
|
302
307
|
*
|
|
303
308
|
* Manifest is cached after first load; .md fetches are cached per key.
|
|
304
309
|
*/
|
|
305
|
-
declare class HttpFaqContentLoader implements FaqContentLoader {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
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[];
|
|
331
335
|
}
|
|
332
336
|
/**
|
|
333
337
|
* Central FAQ state + resolution service.
|
|
@@ -339,82 +343,81 @@ interface FaqResolution {
|
|
|
339
343
|
* - Resolve a dot-notation key against the manifest with parent fallback,
|
|
340
344
|
* audience filtering, and `index` as the terminal fallback.
|
|
341
345
|
*/
|
|
342
|
-
declare class FaqService {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
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
|
-
declare function buildFallbackChain(key: string | null): string[];
|
|
416
|
-
declare function audienceAllowed(audience: FaqAudience, isAuthenticated: boolean): boolean;
|
|
417
|
-
|
|
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;
|
|
418
421
|
/**
|
|
419
422
|
* E8 — In-modal navigation history.
|
|
420
423
|
*
|
|
@@ -434,57 +437,56 @@ declare function audienceAllowed(audience: FaqAudience, isAuthenticated: boolean
|
|
|
434
437
|
* to unwind our entries; suppress in-flight popstates while unwinding;
|
|
435
438
|
* finalize close once we've seen the expected number of popstates.
|
|
436
439
|
*/
|
|
437
|
-
declare class FaqHistoryService {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
479
481
|
interface TocGroup {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
+
category: string;
|
|
483
|
+
entries: FaqEntry[];
|
|
482
484
|
}
|
|
483
485
|
interface SearchResult {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
486
|
+
key: string;
|
|
487
|
+
title: string;
|
|
488
|
+
category: string;
|
|
489
|
+
snippet: string;
|
|
488
490
|
}
|
|
489
491
|
/**
|
|
490
492
|
* E3 — FAQ modal. Two-pane layout (TOC sidebar + content pane), 80% viewport.
|
|
@@ -497,53 +499,52 @@ interface SearchResult {
|
|
|
497
499
|
* returnUrl-aware `authGuard` funnels unauthenticated users through
|
|
498
500
|
* `/login?returnUrl=...`, and `Login` honors returnUrl on success.
|
|
499
501
|
*/
|
|
500
|
-
declare class FaqModal {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
547
548
|
/**
|
|
548
549
|
* Gate for the dev-only FAQ authoring panel (E11).
|
|
549
550
|
*
|
|
@@ -555,21 +556,20 @@ declare class FaqModal {
|
|
|
555
556
|
* The panel is loaded via dynamic `import()` and lives in its own lazy chunk;
|
|
556
557
|
* builds where this resolves to `false` never request the chunk.
|
|
557
558
|
*/
|
|
558
|
-
declare const FAQ_DEV_MODE: InjectionToken<boolean>;
|
|
559
|
+
export declare const FAQ_DEV_MODE: InjectionToken<boolean>;
|
|
559
560
|
/**
|
|
560
561
|
* Optional absolute filesystem root of the consuming app's workspace (e.g.
|
|
561
562
|
* `/Users/foo/dev/broken-stock`). When set, the authoring panel emits
|
|
562
563
|
* `webstorm://open?file=<root>/<relative-path>` deeplinks pointing at the
|
|
563
564
|
* exact source file. When unset, the panel falls back to a relative path.
|
|
564
565
|
*/
|
|
565
|
-
declare const FAQ_DEV_WORKSPACE_ROOT: InjectionToken<string>;
|
|
566
|
+
export declare const FAQ_DEV_WORKSPACE_ROOT: InjectionToken<string>;
|
|
566
567
|
/**
|
|
567
568
|
* Editor URL scheme for the "Open in editor" deeplink. Defaults to WebStorm.
|
|
568
569
|
* Set to `'vscode'` (or any custom scheme handler) per consuming app.
|
|
569
570
|
*/
|
|
570
|
-
type FaqDevEditor = 'webstorm' | 'vscode';
|
|
571
|
-
declare const FAQ_DEV_EDITOR: InjectionToken<FaqDevEditor>;
|
|
572
|
-
|
|
571
|
+
export type FaqDevEditor = 'webstorm' | 'vscode';
|
|
572
|
+
export declare const FAQ_DEV_EDITOR: InjectionToken<FaqDevEditor>;
|
|
573
573
|
/**
|
|
574
574
|
* E11 — Dev-only FAQ authoring panel.
|
|
575
575
|
*
|
|
@@ -582,45 +582,65 @@ declare const FAQ_DEV_EDITOR: InjectionToken<FaqDevEditor>;
|
|
|
582
582
|
* The panel never imports `environment.*` directly — gating is the modal's
|
|
583
583
|
* responsibility (via the FAQ_DEV_MODE token + dynamic import).
|
|
584
584
|
*/
|
|
585
|
-
declare class FaqAuthoringPanel {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
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
|
+
}
|
|
621
641
|
interface CategoryGroup$1 {
|
|
622
|
-
|
|
623
|
-
|
|
642
|
+
category: string;
|
|
643
|
+
entries: FaqEntry[];
|
|
624
644
|
}
|
|
625
645
|
/**
|
|
626
646
|
* Public, SEO-targeted FAQ index. Prerendered at build time on the
|
|
@@ -632,23 +652,22 @@ interface CategoryGroup$1 {
|
|
|
632
652
|
* JSON-LD (E18): emits a schema.org CollectionPage referencing
|
|
633
653
|
* each child entry as an ItemList.
|
|
634
654
|
*/
|
|
635
|
-
declare class PublicFaqIndex implements OnInit {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
}
|
|
651
|
-
|
|
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
|
+
}
|
|
652
671
|
/**
|
|
653
672
|
* Public, SEO-targeted FAQ page. Prerendered at build time per
|
|
654
673
|
* qualifying manifest entry (e.g. /help/landing, /help/trade/segments).
|
|
@@ -663,31 +682,30 @@ declare class PublicFaqIndex implements OnInit {
|
|
|
663
682
|
* raw JSON has `<` escaped to `<` so a stray `</script>` inside
|
|
664
683
|
* data won't break the surrounding tag.
|
|
665
684
|
*/
|
|
666
|
-
declare class PublicFaqPage implements OnInit {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
}
|
|
687
|
-
|
|
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
|
+
}
|
|
688
706
|
interface CategoryGroup {
|
|
689
|
-
|
|
690
|
-
|
|
707
|
+
category: string;
|
|
708
|
+
entries: FaqEntry[];
|
|
691
709
|
}
|
|
692
710
|
/**
|
|
693
711
|
* Shared TOC sidebar for the public /help pages. Lists every
|
|
@@ -698,17 +716,22 @@ interface CategoryGroup {
|
|
|
698
716
|
* because the index page (`/help`) and entry pages
|
|
699
717
|
* (`/help/<file>`) all share this sidebar.
|
|
700
718
|
*/
|
|
701
|
-
declare class PublicFaqToc implements OnInit {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
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
|
+
}
|
|
712
735
|
/**
|
|
713
736
|
* Canonical public base URL (no trailing slash) used for OpenGraph,
|
|
714
737
|
* Twitter, JSON-LD canonical URLs, and sitemap entries on the
|
|
@@ -716,86 +739,81 @@ declare class PublicFaqToc implements OnInit {
|
|
|
716
739
|
* `og:url` etc. when unset, which is fine for previewing but
|
|
717
740
|
* suboptimal for production SEO. Consumer apps should override.
|
|
718
741
|
*/
|
|
719
|
-
declare const FAQ_PUBLIC_SITE_BASE_URL: InjectionToken<string>;
|
|
742
|
+
export declare const FAQ_PUBLIC_SITE_BASE_URL: InjectionToken<string>;
|
|
720
743
|
/**
|
|
721
744
|
* Publisher / author Organization embedded in schema.org JSON-LD on
|
|
722
745
|
* public FAQ pages. `logo` is optional — Google's validator warns but
|
|
723
746
|
* does not error when omitted. Add it later via a one-line provider
|
|
724
747
|
* override.
|
|
725
748
|
*/
|
|
726
|
-
interface FaqPublicPublisher {
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
}
|
|
731
|
-
declare const FAQ_PUBLIC_PUBLISHER: InjectionToken<FaqPublicPublisher>;
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
}
|
|
742
|
-
|
|
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
|
+
}
|
|
743
764
|
/** Deliberately outside `bs.pref.` — see the header. */
|
|
744
|
-
declare const SESSION_TOKEN_KEY = "bs.auth.token";
|
|
745
|
-
declare class SessionTokenStore {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
}
|
|
777
|
-
|
|
778
|
-
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 =
|
|
779
799
|
/** The token is gone: the window that renewed failed permanently, or signed out. */
|
|
780
|
-
'sign-out'
|
|
800
|
+
'sign-out' |
|
|
781
801
|
/** Another window renewed while we queued. Take its token; do NOT rotate again. */
|
|
782
|
-
|
|
802
|
+
'adopt' |
|
|
783
803
|
/** Nothing has moved. This window performs the renewal itself. */
|
|
784
|
-
|
|
804
|
+
'renew';
|
|
785
805
|
/**
|
|
786
806
|
* @param before the token this window held immediately BEFORE asking for the lock
|
|
787
807
|
* @param current the token in shared storage now that this window can act
|
|
788
808
|
*/
|
|
789
|
-
declare function decideRenewal(before: BearerToken | undefined, current: BearerToken | undefined): RenewalDecision;
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
}
|
|
798
|
-
|
|
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
|
+
}
|
|
799
817
|
/**
|
|
800
818
|
* Asks the command box to take the cursor. Called by the shell after most
|
|
801
819
|
* actions (rail choices, Log Out, ?, ⧉, Escape, sign-in) and by apps.
|
|
@@ -807,14 +825,13 @@ declare class ThemeService {
|
|
|
807
825
|
* is refused. Refusing it at the source also means a request made on a phone is
|
|
808
826
|
* not left counted up, to be acted on after a rotation to a wider screen.
|
|
809
827
|
*/
|
|
810
|
-
declare class TerminalFocusService {
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
}
|
|
817
|
-
|
|
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
|
+
}
|
|
818
835
|
/**
|
|
819
836
|
* Controls visibility of the command terminal at the bottom of the shell.
|
|
820
837
|
*
|
|
@@ -856,21 +873,20 @@ declare class TerminalFocusService {
|
|
|
856
873
|
* a toggle while a screen has it hidden shows it again, and saves nothing, because the saved
|
|
857
874
|
* choice was "visible" all along.
|
|
858
875
|
*/
|
|
859
|
-
declare class TerminalVisibilityService {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
}
|
|
873
|
-
|
|
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
|
+
}
|
|
874
890
|
/**
|
|
875
891
|
* Below this width the frame is in its phone layout: no icon column, a ☰ menu
|
|
876
892
|
* button in the top bar, a one-line top bar.
|
|
@@ -883,26 +899,25 @@ declare class TerminalVisibilityService {
|
|
|
883
899
|
* a media query cannot read a TypeScript constant. phone-layout.service.spec.ts
|
|
884
900
|
* fails if any of them drifts from this value.
|
|
885
901
|
*/
|
|
886
|
-
declare const SHELL_PHONE_MEDIA_QUERY = "(max-width: 819.98px)";
|
|
902
|
+
export declare const SHELL_PHONE_MEDIA_QUERY = "(max-width: 819.98px)";
|
|
887
903
|
/**
|
|
888
904
|
* Whether the frame is in its phone layout, and whether the phone menu is open.
|
|
889
905
|
*
|
|
890
906
|
* Desktop never sees any of this: `isPhone` is false there, the menu cannot open,
|
|
891
907
|
* and the rail renders exactly as before.
|
|
892
908
|
*/
|
|
893
|
-
declare class PhoneLayoutService {
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
}
|
|
905
|
-
|
|
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
|
+
}
|
|
906
921
|
/**
|
|
907
922
|
* A key binding a screen registers while it is mounted.
|
|
908
923
|
*
|
|
@@ -914,134 +929,131 @@ declare class PhoneLayoutService {
|
|
|
914
929
|
* work and then breaks on any non-US keyboard. `code: 'Digit1'` is the physical
|
|
915
930
|
* key and is layout-independent.
|
|
916
931
|
*/
|
|
917
|
-
interface ShortcutRegistration {
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
}
|
|
929
|
-
declare class KeyboardShortcutService {
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
}
|
|
954
|
-
|
|
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
|
+
}
|
|
955
969
|
/** A key combination: the physical key and the modifiers that must be held, exactly. */
|
|
956
|
-
interface ScreenKeyCombo {
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
}
|
|
962
|
-
interface ScreenKeyRegistration {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
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;
|
|
967
981
|
}
|
|
968
982
|
/** The class the command terminal's input carries; keys typed there still reach a screen's keys. */
|
|
969
|
-
declare const TERMINAL_INPUT_CLASS = "terminal-input";
|
|
983
|
+
export declare const TERMINAL_INPUT_CLASS = "terminal-input";
|
|
970
984
|
/** True when the key is typed into a box other than the command terminal. */
|
|
971
|
-
declare function isTypingElsewhere(target: EventTarget | null): boolean;
|
|
985
|
+
export declare function isTypingElsewhere(target: EventTarget | null): boolean;
|
|
972
986
|
/** Exactly this combination: the key, and these modifiers and no others. */
|
|
973
|
-
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;
|
|
974
988
|
/** Whether a keydown may reach a screen's keys at all. */
|
|
975
|
-
declare function isEligibleKey(e: Pick<KeyboardEvent, 'repeat' | 'isComposing' | 'target'>): boolean;
|
|
976
|
-
declare class ScreenKeysService {
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
}
|
|
984
|
-
|
|
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
|
+
}
|
|
985
998
|
/** The `reason` the auth worker puts on every authenticator refusal. */
|
|
986
|
-
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';
|
|
987
1000
|
/** Which extra step the sign-in screen is on, after the password was accepted. */
|
|
988
|
-
type SignInCodeStep = 'none' | 'code' | 'setup';
|
|
1001
|
+
export type SignInCodeStep = 'none' | 'code' | 'setup';
|
|
989
1002
|
/** GET {login}/authenticator. */
|
|
990
|
-
type AuthenticatorStatus = {
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
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;
|
|
996
1009
|
};
|
|
997
1010
|
/** What a code-bearing request carries: a 6-digit code or a recovery code. */
|
|
998
|
-
type CodeEntry = {
|
|
999
|
-
|
|
1000
|
-
|
|
1011
|
+
export type CodeEntry = {
|
|
1012
|
+
code?: string;
|
|
1013
|
+
recoveryCode?: string;
|
|
1001
1014
|
};
|
|
1002
1015
|
/** The QR shown once during setup, and the same secret as text for typing in by hand. */
|
|
1003
|
-
type SetupChallenge = {
|
|
1004
|
-
|
|
1005
|
-
|
|
1016
|
+
export type SetupChallenge = {
|
|
1017
|
+
qr: string;
|
|
1018
|
+
key: string;
|
|
1006
1019
|
};
|
|
1007
1020
|
/**
|
|
1008
1021
|
* The one message for a pause (D8). The server sends no time and the screen invents none.
|
|
1009
1022
|
*/
|
|
1010
|
-
declare const TOO_MANY_ATTEMPTS = "Too many attempts.";
|
|
1023
|
+
export declare const TOO_MANY_ATTEMPTS = "Too many attempts.";
|
|
1011
1024
|
/** Plain words for each refusal, shown on the sign-in screen and in the code dialog. */
|
|
1012
|
-
declare function authenticatorMessage(reason: AuthenticatorReason | undefined): string;
|
|
1025
|
+
export declare function authenticatorMessage(reason: AuthenticatorReason | undefined): string;
|
|
1013
1026
|
/** The text file the Download button saves (D7). */
|
|
1014
|
-
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;
|
|
1015
1028
|
/** One of the user's passkeys, as GET {login}/passkeys lists it (passkey-sign-in.prd.md D13, D17). */
|
|
1016
|
-
type PasskeySummary = {
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
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;
|
|
1023
1036
|
};
|
|
1024
1037
|
/**
|
|
1025
1038
|
* What proves you are at the keyboard right now, for adding or removing a passkey (D14, D15):
|
|
1026
1039
|
* password + code (or recovery code), a code alone (removing only), or one of your passkeys.
|
|
1027
1040
|
*/
|
|
1028
|
-
type PasskeyProof = (CodeEntry & {
|
|
1029
|
-
|
|
1041
|
+
export type PasskeyProof = (CodeEntry & {
|
|
1042
|
+
password?: string;
|
|
1030
1043
|
}) | 'passkey';
|
|
1031
|
-
|
|
1032
1044
|
/**
|
|
1033
1045
|
* A refusal from an authenticator route, carrying the server's `reason`
|
|
1034
1046
|
* (authenticator-sign-in.prd.md). `message` is plain words for the screen.
|
|
1035
1047
|
*/
|
|
1036
|
-
declare class AuthenticatorError extends Error {
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
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);
|
|
1040
1052
|
}
|
|
1041
1053
|
/** Sign-in rejected because the next step is a code (or setting one up), not because anything failed. */
|
|
1042
|
-
declare const CODE_STEP = "code-step";
|
|
1054
|
+
export declare const CODE_STEP = "code-step";
|
|
1043
1055
|
/** The user dismissed the passkey prompt — nothing failed, nothing to say. */
|
|
1044
|
-
declare const PASSKEY_CANCELLED = "passkey-cancelled";
|
|
1056
|
+
export declare const PASSKEY_CANCELLED = "passkey-cancelled";
|
|
1045
1057
|
/**
|
|
1046
1058
|
* What the user decided about sessions they already hold, sent on the SECOND
|
|
1047
1059
|
* sign-in attempt (multiple-devices D2/D6).
|
|
@@ -1050,14 +1062,14 @@ declare const PASSKEY_CANCELLED = "passkey-cancelled";
|
|
|
1050
1062
|
* over everything is `force` on the call itself, signing in beside them is
|
|
1051
1063
|
* `additional`, and ending a chosen few is `endSessionIds`.
|
|
1052
1064
|
*/
|
|
1053
|
-
type LoginChoice = {
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
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;
|
|
1061
1073
|
};
|
|
1062
1074
|
/**
|
|
1063
1075
|
* The sign-in request body, exactly as sent (multiple-devices E8, authenticator-sign-in.prd.md).
|
|
@@ -1067,591 +1079,606 @@ type LoginChoice = {
|
|
|
1067
1079
|
* meant. A code typed now wins; otherwise a ticket from a 409 that followed a
|
|
1068
1080
|
* correct code stands in for it (the server accepts a code only once, D20).
|
|
1069
1081
|
*/
|
|
1070
|
-
declare function buildLoginBody(credentials: Credentials, force: boolean, options: LoginChoice, signInTicket?: string): Record<string, unknown>;
|
|
1071
|
-
declare class AuthenticationService {
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
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
|
-
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;
|
|
1201
1212
|
/** Whether the browser can offer passkeys in the username box's suggestions (D9). */
|
|
1202
|
-
declare function passkeyAutofillSupported(): Promise<boolean>;
|
|
1203
|
-
|
|
1213
|
+
export declare function passkeyAutofillSupported(): Promise<boolean>;
|
|
1204
1214
|
/**
|
|
1205
1215
|
* What a typed word resolves to: the command it names, plus any words the
|
|
1206
1216
|
* alternate name carries with it (see {@link CommandAlias}).
|
|
1207
1217
|
*/
|
|
1208
|
-
interface ResolvedCommand {
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
}
|
|
1212
|
-
declare class CommandRegistryService {
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
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
|
-
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;
|
|
1262
1271
|
}
|
|
1263
1272
|
/** True for the answers that mean yes: a bare Enter, `y`, `yes`. */
|
|
1264
|
-
declare function isYes(text: string): boolean;
|
|
1273
|
+
export declare function isYes(text: string): boolean;
|
|
1265
1274
|
/** What the history shows for an answer: the text, a masked run, or "(Enter)". */
|
|
1266
|
-
declare function answerEcho(prompt: PendingPrompt, text: string): string;
|
|
1275
|
+
export declare function answerEcho(prompt: PendingPrompt, text: string): string;
|
|
1267
1276
|
/** Holds at most one open question. Asking again cancels the one before it. */
|
|
1268
|
-
declare class PendingPromptState {
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
1311
1318
|
/**
|
|
1312
1319
|
* Quote-aware command tokenizer. Treats "..." as single tokens and strips enclosing quotes.
|
|
1313
1320
|
*/
|
|
1314
|
-
declare function tokenize(input: string): string[];
|
|
1315
|
-
declare const CLEAR_USAGE: string;
|
|
1316
|
-
declare const HELP_USAGE: string;
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
1578
1609
|
type Mode = 'view' | 'password' | 'scan' | 'reissue' | 'codes' | 'passkey-add' | 'passkey-remove';
|
|
1579
|
-
declare class AuthenticatorSettings implements OnInit {
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
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
|
-
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>;
|
|
1637
1667
|
}
|
|
1638
1668
|
/** Opens the dialog; resolves with the recorded time (epoch ms), or undefined when cancelled. */
|
|
1639
|
-
declare function openCodeDialog(dialog: MatDialog, data: CodeDialogData): Promise<number | undefined>;
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
}
|
|
1654
|
-
|
|
1655
|
-
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 };
|
|
1656
|
-
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, MenuItem, ParsedFaq, PasskeyProof, PasskeySummary, PendingPrompt, RenewalDecision, ResolvedCommand, RouteScope, ScreenKeyCombo, ScreenKeyRegistration, SetupChallenge, Severity, ShellAuthUrls, ShellConfig, ShortcutRegistration, SignInCodeStep, ThemeMode };
|
|
1657
|
-
//# 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
|