@coolms/core-angular 2.0.0-alpha.5 → 2.0.0-alpha.6

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,166 +1,164 @@
1
- import * as rxjs from 'rxjs';
2
- import { Observable, Subject } from 'rxjs';
3
- import * as i0 from '@angular/core';
4
- import { InjectionToken, Type, Provider, EnvironmentProviders } from '@angular/core';
5
- import { StateContext } from '@ngxs/store';
6
- import { CanActivateFn, Routes, CanMatchFn } from '@angular/router';
7
- import { HttpContextToken, HttpInterceptorFn } from '@angular/common/http';
8
-
9
- interface AuthApiManifest {
10
- readonly login: string;
11
- readonly logout: string;
12
- readonly refresh: string;
13
- readonly register: string;
14
- readonly me: string;
15
- readonly usersApi?: string;
16
- readonly groupsApi?: string;
17
- readonly preferencesUrl?: string;
18
- readonly groupFormId?: string | null;
19
- readonly userFormId?: string | null;
20
- }
21
- interface SectionApiManifest {
22
- readonly list: string;
23
- readonly create: string;
24
- readonly item: string;
25
- readonly update: string;
26
- readonly delete: string;
27
- readonly formId?: string;
28
- readonly apply?: string;
29
- }
30
- interface NaviApiManifest {
31
- readonly treesList: string;
32
- readonly treesCreate: string;
33
- readonly treesItem: string;
34
- readonly nodesList: string;
35
- readonly nodesCreate: string;
36
- readonly nodesItem: string;
37
- readonly nodesReorder: string;
38
- readonly nodesMove: string;
39
- readonly nodesByTree: string;
40
- readonly treesFormId?: string;
41
- readonly nodesFormId?: string;
42
- readonly adminNavGraph?: string;
43
- readonly topbarNavGraph?: string;
44
- readonly toolbarVfsGraph?: string;
45
- readonly graphBySlug?: string;
46
- }
47
- interface ContentApiManifest {
48
- readonly variantPublishUrl: string;
49
- readonly pageSpacesUrl?: string;
50
- readonly pageTypesUrl?: string;
51
- }
52
- interface DataGridApiManifest {
53
- readonly configBase: string;
54
- }
55
- interface TerminalApiManifest {
56
- readonly executeUrl: string;
57
- readonly completeUrl: string;
58
- }
59
- interface MediaApiManifest {
60
- readonly listUrl: string;
61
- readonly itemUrl: string;
62
- readonly permissionsUrl: string;
63
- readonly regenerateUrl: string;
64
- readonly resizeUrl: string;
65
- readonly collectionsUrl: string;
66
- readonly collectionsCreateUrl: string;
67
- readonly collectionPermissionsUrl: string;
68
- readonly moveUrl?: string;
69
- readonly spacesUrl?: string;
70
- }
71
- interface VfsApiManifest {
72
- readonly fileContentUrl: string;
73
- readonly binaryWriteUrl: string;
1
+ import { Observable, Subject } from "rxjs";
2
+ import * as i0 from "@angular/core";
3
+ import { EnvironmentProviders, InjectionToken, Provider, Type } from "@angular/core";
4
+ import { StateContext } from "@ngxs/store";
5
+ import { CanActivateFn, CanMatchFn, Routes } from "@angular/router";
6
+ import { HttpContextToken, HttpInterceptorFn, HttpRequest } from "@angular/common/http";
7
+ export interface AuthApiManifest {
8
+ readonly login: string;
9
+ readonly logout: string;
10
+ readonly refresh: string;
11
+ readonly register: string;
12
+ readonly me: string;
13
+ readonly usersApi?: string;
14
+ readonly groupsApi?: string;
15
+ readonly preferencesUrl?: string;
16
+ readonly groupFormId?: string | null;
17
+ readonly userFormId?: string | null;
18
+ }
19
+ export interface SectionApiManifest {
20
+ readonly list: string;
21
+ readonly create: string;
22
+ readonly item: string;
23
+ readonly update: string;
24
+ readonly delete: string;
25
+ readonly formId?: string;
26
+ readonly apply?: string;
27
+ }
28
+ export interface NaviApiManifest {
29
+ readonly treesList: string;
30
+ readonly treesCreate: string;
31
+ readonly treesItem: string;
32
+ readonly nodesList: string;
33
+ readonly nodesCreate: string;
34
+ readonly nodesItem: string;
35
+ readonly nodesReorder: string;
36
+ readonly nodesMove: string;
37
+ readonly nodesByTree: string;
38
+ readonly treesFormId?: string;
39
+ readonly nodesFormId?: string;
40
+ readonly adminNavGraph?: string;
41
+ readonly topbarNavGraph?: string;
42
+ readonly toolbarVfsGraph?: string;
43
+ readonly graphBySlug?: string;
44
+ }
45
+ export interface ContentApiManifest {
46
+ readonly variantPublishUrl: string;
47
+ readonly pageSpacesUrl?: string;
48
+ readonly pageTypesUrl?: string;
49
+ }
50
+ export interface DataGridApiManifest {
51
+ readonly configBase: string;
52
+ }
53
+ export interface TerminalApiManifest {
54
+ readonly executeUrl: string;
55
+ readonly completeUrl: string;
56
+ }
57
+ export interface MediaApiManifest {
58
+ readonly listUrl: string;
59
+ readonly itemUrl: string;
60
+ readonly permissionsUrl: string;
61
+ readonly regenerateUrl: string;
62
+ readonly resizeUrl: string;
63
+ readonly collectionsUrl: string;
64
+ readonly collectionsCreateUrl: string;
65
+ readonly collectionPermissionsUrl: string;
66
+ readonly moveUrl?: string;
67
+ readonly spacesUrl?: string;
68
+ }
69
+ export interface VfsApiManifest {
70
+ readonly fileContentUrl: string;
71
+ readonly binaryWriteUrl: string;
74
72
  }
75
73
  /**
76
74
  * Document module URL section. Today the only entry is `spacesUrl`;
77
75
  * the struct is reserved so future Document-module URLs can land
78
76
  * here without a new manifest section.
79
77
  */
80
- interface DocumentApiManifest {
81
- readonly spacesUrl?: string;
82
- /**
83
- * !! Carried here rather than derived from `spacesUrl`. Concatenating
84
- * `/available` onto that would couple the client to a URL shape the router
85
- * owns, and it would keep working until the route moved -- then fail as a
86
- * 404 the UI reports as "no sites available", which is indistinguishable
87
- * from the true empty answer.
88
- *
89
- * !! ABSENT ARRIVES AS `''`, NOT `undefined`. The producer declares these
90
- * as non-nullable strings defaulting to empty, so an installation that has
91
- * not set them sends an empty string and the `?` here never fires.
92
- *
93
- * Guard on FALSINESS -- `!url`, `Boolean(url)`. The dangerous rewrite is
94
- * `url === undefined` (or `!== undefined`), which reads as more precise and
95
- * treats `''` as present, handing a caller a request to the empty string.
96
- *
97
- * !! A `?? fallback` is NOT the hazard, though it looks like the obvious
98
- * one: `'' ?? x` is `''`, so a fallback never fires on the value this
99
- * producer actually sends. Measured by mutation -- adding one changed
100
- * nothing, changing the guard broke three assertions.
101
- */
102
- readonly spacesAvailableUrl?: string;
103
- readonly spaceEnablementUrl?: string;
104
- /**
105
- * FQCN of the entity a filter-mode audience can be built from.
106
- *
107
- * !! The SERVER owns this and the client must not know it. The generation
108
- * wizard offers Filter mode only when a template's context schema
109
- * references this type, and it used to compare against a constant compiled
110
- * into this bundle -- which shipped the consuming application's class name
111
- * inside a published package, and tied that package to an installation
112
- * having that exact class.
113
- *
114
- * !! Absent arrives as `''`, like the urls above. Treat empty as **filter
115
- * mode unavailable**, never as a reason to fall back to a remembered
116
- * value: the server is the thing that rejects every other type, so a guess
117
- * here offers a mode the server will refuse.
118
- */
119
- readonly filterAudienceEntity?: string;
120
- }
121
- interface DynamicEntityApiManifest {
122
- readonly typesUrl: string;
123
- readonly fieldDefinitionsUrl: string;
124
- readonly fieldDefinitionUrl: string;
125
- readonly fieldDefinitionsReorderUrl: string;
126
- readonly toolbarNaviGraphUrl: string;
127
- readonly constraintsUrl: string;
128
- readonly recordsUrl: string;
129
- readonly recordUrl: string;
130
- readonly recordsByTypeUrl: string;
131
- readonly typeUrl: string;
132
- readonly typesCreateUrl: string;
133
- readonly formTypesUrl?: string;
134
- readonly typeByAliasUrl?: string;
135
- }
136
- interface DomainExplorerApiManifest {
137
- readonly entitiesUrl: string;
138
- readonly entityUrl: string;
139
- readonly toolbarNaviGraphUrl: string;
140
- readonly domainExplorerToolbarUrl: string;
141
- readonly entityFieldsDataGridUrl?: string;
142
- readonly typeFieldsDataGridUrl?: string;
143
- }
144
- interface IdentityApiManifest {
145
- readonly usersUrl: string;
146
- readonly userUrl: string;
147
- readonly groupsUrl: string;
148
- readonly groupUrl: string;
149
- readonly meUrl: string;
150
- readonly assignGroupsUrl: string;
151
- readonly avatarUploadUrl: string;
152
- readonly settingsUrl: string;
153
- readonly settingsSectionsUrl: string;
154
- readonly settingsSectionUrl: string;
155
- readonly colorUrl: string;
156
- readonly rolesUrl?: string;
157
- readonly elevationUrl?: string;
158
- readonly deletionsUrl?: string;
159
- readonly footprintsUrl?: string;
160
- readonly userDeletionUrl?: string;
161
- readonly userLegalHoldsUrl?: string;
162
- readonly holdsSettingsBlock?: string;
163
- readonly legalHoldFormId?: string | null;
78
+ export interface DocumentApiManifest {
79
+ readonly spacesUrl?: string;
80
+ /**
81
+ * !! Carried here rather than derived from `spacesUrl`. Concatenating
82
+ * `/available` onto that would couple the client to a URL shape the router
83
+ * owns, and it would keep working until the route moved -- then fail as a
84
+ * 404 the UI reports as "no sites available", which is indistinguishable
85
+ * from the true empty answer.
86
+ *
87
+ * !! ABSENT ARRIVES AS `''`, NOT `undefined`. The producer declares these
88
+ * as non-nullable strings defaulting to empty, so an installation that has
89
+ * not set them sends an empty string and the `?` here never fires.
90
+ *
91
+ * Guard on FALSINESS -- `!url`, `Boolean(url)`. The dangerous rewrite is
92
+ * `url === undefined` (or `!== undefined`), which reads as more precise and
93
+ * treats `''` as present, handing a caller a request to the empty string.
94
+ *
95
+ * !! A `?? fallback` is NOT the hazard, though it looks like the obvious
96
+ * one: `'' ?? x` is `''`, so a fallback never fires on the value this
97
+ * producer actually sends. Measured by mutation -- adding one changed
98
+ * nothing, changing the guard broke three assertions.
99
+ */
100
+ readonly spacesAvailableUrl?: string;
101
+ readonly spaceEnablementUrl?: string;
102
+ /**
103
+ * FQCN of the entity a filter-mode audience can be built from.
104
+ *
105
+ * !! The SERVER owns this and the client must not know it. The generation
106
+ * wizard offers Filter mode only when a template's context schema
107
+ * references this type, and it used to compare against a constant compiled
108
+ * into this bundle -- which shipped the consuming application's class name
109
+ * inside a published package, and tied that package to an installation
110
+ * having that exact class.
111
+ *
112
+ * !! Absent arrives as `''`, like the urls above. Treat empty as **filter
113
+ * mode unavailable**, never as a reason to fall back to a remembered
114
+ * value: the server is the thing that rejects every other type, so a guess
115
+ * here offers a mode the server will refuse.
116
+ */
117
+ readonly filterAudienceEntity?: string;
118
+ }
119
+ export interface DynamicEntityApiManifest {
120
+ readonly typesUrl: string;
121
+ readonly fieldDefinitionsUrl: string;
122
+ readonly fieldDefinitionUrl: string;
123
+ readonly fieldDefinitionsReorderUrl: string;
124
+ readonly toolbarNaviGraphUrl: string;
125
+ readonly constraintsUrl: string;
126
+ readonly recordsUrl: string;
127
+ readonly recordUrl: string;
128
+ readonly recordsByTypeUrl: string;
129
+ readonly typeUrl: string;
130
+ readonly typesCreateUrl: string;
131
+ readonly formTypesUrl?: string;
132
+ readonly typeByAliasUrl?: string;
133
+ }
134
+ export interface DomainExplorerApiManifest {
135
+ readonly entitiesUrl: string;
136
+ readonly entityUrl: string;
137
+ readonly toolbarNaviGraphUrl: string;
138
+ readonly domainExplorerToolbarUrl: string;
139
+ readonly entityFieldsDataGridUrl?: string;
140
+ readonly typeFieldsDataGridUrl?: string;
141
+ }
142
+ export interface IdentityApiManifest {
143
+ readonly usersUrl: string;
144
+ readonly userUrl: string;
145
+ readonly groupsUrl: string;
146
+ readonly groupUrl: string;
147
+ readonly meUrl: string;
148
+ readonly assignGroupsUrl: string;
149
+ readonly avatarUploadUrl: string;
150
+ readonly settingsUrl: string;
151
+ readonly settingsSectionsUrl: string;
152
+ readonly settingsSectionUrl: string;
153
+ readonly colorUrl: string;
154
+ readonly rolesUrl?: string;
155
+ readonly elevationUrl?: string;
156
+ readonly deletionsUrl?: string;
157
+ readonly footprintsUrl?: string;
158
+ readonly userDeletionUrl?: string;
159
+ readonly userLegalHoldsUrl?: string;
160
+ readonly holdsSettingsBlock?: string;
161
+ readonly legalHoldFormId?: string | null;
164
162
  }
165
163
  /**
166
164
  * Editor sub-manifest emitted by the backend's Editor module. The
@@ -172,27 +170,27 @@ interface IdentityApiManifest {
172
170
  * pre-resolved contributor list AND the storage allow-list a sanitizer
173
171
  * enforces on save.
174
172
  */
175
- interface EditorApiManifestEntry {
176
- readonly id: string;
177
- readonly group: string;
178
- readonly priority: number;
179
- readonly icon: string;
180
- readonly label: string;
181
- readonly actionType: string;
182
- readonly actionParams: Readonly<Record<string, unknown>>;
183
- readonly extensions: ReadonlyArray<string>;
184
- readonly stateKeys: ReadonlyArray<string>;
185
- readonly shortcut?: string | null;
186
- readonly slashable?: boolean;
187
- readonly keywords?: ReadonlyArray<string>;
188
- }
189
- interface EditorApiManifestProfile {
190
- readonly contributors: ReadonlyArray<EditorApiManifestEntry>;
191
- readonly allowedWidgets: ReadonlyArray<string>;
192
- }
193
- interface EditorApiManifest {
194
- readonly schemaVersion: number;
195
- readonly profiles: Readonly<Record<string, EditorApiManifestProfile>>;
173
+ export interface EditorApiManifestEntry {
174
+ readonly id: string;
175
+ readonly group: string;
176
+ readonly priority: number;
177
+ readonly icon: string;
178
+ readonly label: string;
179
+ readonly actionType: string;
180
+ readonly actionParams: Readonly<Record<string, unknown>>;
181
+ readonly extensions: ReadonlyArray<string>;
182
+ readonly stateKeys: ReadonlyArray<string>;
183
+ readonly shortcut?: string | null;
184
+ readonly slashable?: boolean;
185
+ readonly keywords?: ReadonlyArray<string>;
186
+ }
187
+ export interface EditorApiManifestProfile {
188
+ readonly contributors: ReadonlyArray<EditorApiManifestEntry>;
189
+ readonly allowedWidgets: ReadonlyArray<string>;
190
+ }
191
+ export interface EditorApiManifest {
192
+ readonly schemaVersion: number;
193
+ readonly profiles: Readonly<Record<string, EditorApiManifestProfile>>;
196
194
  }
197
195
  /**
198
196
  * Viewer manifest emitted by the backend's Document module.
@@ -201,20 +199,20 @@ interface EditorApiManifest {
201
199
  * profile. Profile config is open-ended -- every viewer interprets the
202
200
  * keys it understands and ignores the rest.
203
201
  */
204
- interface ViewerProfileApiManifest {
205
- readonly key: string;
206
- readonly config: Readonly<Record<string, unknown>>;
202
+ export interface ViewerProfileApiManifest {
203
+ readonly key: string;
204
+ readonly config: Readonly<Record<string, unknown>>;
207
205
  }
208
- interface ViewerDefinitionApiManifest {
209
- readonly key: string;
210
- readonly mimeTypes: ReadonlyArray<string>;
211
- readonly extensions: ReadonlyArray<string>;
212
- readonly component: string;
213
- readonly profiles: Readonly<Record<string, ViewerProfileApiManifest>>;
206
+ export interface ViewerDefinitionApiManifest {
207
+ readonly key: string;
208
+ readonly mimeTypes: ReadonlyArray<string>;
209
+ readonly extensions: ReadonlyArray<string>;
210
+ readonly component: string;
211
+ readonly profiles: Readonly<Record<string, ViewerProfileApiManifest>>;
214
212
  }
215
- interface ViewerApiManifest {
216
- readonly schemaVersion: number;
217
- readonly viewers: Readonly<Record<string, ViewerDefinitionApiManifest>>;
213
+ export interface ViewerApiManifest {
214
+ readonly schemaVersion: number;
215
+ readonly viewers: Readonly<Record<string, ViewerDefinitionApiManifest>>;
218
216
  }
219
217
  /**
220
218
  * Platform-wide default user-facing settings. The
@@ -223,17 +221,17 @@ interface ViewerApiManifest {
223
221
  * config, not an FE-hardcoded Western default. `locale` is the default
224
222
  * locale, previously absent from the manifest.
225
223
  */
226
- interface PlatformDefaults {
227
- readonly locale: string;
228
- readonly timezone: string;
229
- readonly dateFormat: string;
230
- readonly timeFormat: '12h' | '24h';
231
- readonly weekStart: 'monday' | 'sunday';
232
- /**
233
- * Deployment brand accent as `#rrggbb`, or null/absent to keep the
234
- * stylesheet's own. Sits BELOW a user's personal accentColor.
235
- */
236
- readonly accentColor?: string | null;
224
+ export interface PlatformDefaults {
225
+ readonly locale: string;
226
+ readonly timezone: string;
227
+ readonly dateFormat: string;
228
+ readonly timeFormat: '12h' | '24h';
229
+ readonly weekStart: 'monday' | 'sunday';
230
+ /**
231
+ * Deployment brand accent as `#rrggbb`, or null/absent to keep the
232
+ * stylesheet's own. Sits BELOW a user's personal accentColor.
233
+ */
234
+ readonly accentColor?: string | null;
237
235
  }
238
236
  /**
239
237
  * The host contracts in force here (the platform rule: hosts implement
@@ -242,42 +240,42 @@ interface PlatformDefaults {
242
240
  * entries the installer matched against it. A host mounts a module only when
243
241
  * it is listed.
244
242
  */
245
- interface UiApiManifest {
246
- /** The contracts in force, name -> MAJOR.MINOR (`{ console: '1.0' }`): one installed theme implements each. */
247
- readonly contracts: Readonly<Record<string, string>>;
248
- /** Which installed theme implements each contract, name -> theme slug. */
249
- readonly hosts?: Readonly<Record<string, string>>;
250
- readonly modules: ReadonlyArray<UiModuleEntryManifest>;
251
- }
252
- interface UiModuleEntryManifest {
253
- readonly module: string;
254
- readonly contract: string;
255
- readonly range: string;
256
- readonly framework: string;
257
- }
258
- interface ApiManifest {
259
- readonly apiBase: string;
260
- readonly ui?: UiApiManifest;
261
- readonly configBase?: string;
262
- readonly auth?: AuthApiManifest;
263
- readonly identity?: IdentityApiManifest;
264
- readonly sections?: SectionApiManifest;
265
- readonly navi?: NaviApiManifest;
266
- readonly content?: ContentApiManifest;
267
- readonly dataGrid?: DataGridApiManifest;
268
- readonly terminal?: TerminalApiManifest;
269
- readonly media?: MediaApiManifest;
270
- readonly document?: DocumentApiManifest;
271
- readonly vfs?: VfsApiManifest;
272
- readonly dynamicEntity?: DynamicEntityApiManifest;
273
- readonly domainExplorer?: DomainExplorerApiManifest;
274
- readonly editor?: EditorApiManifest;
275
- readonly viewers?: ViewerApiManifest;
276
- readonly supportedLocales?: Array<{
277
- code: string;
278
- label: string;
279
- }>;
280
- readonly platformDefaults?: PlatformDefaults;
243
+ export interface UiApiManifest {
244
+ /** The contracts in force, name -> MAJOR.MINOR (`{ console: '1.0' }`): one installed theme implements each. */
245
+ readonly contracts: Readonly<Record<string, string>>;
246
+ /** Which installed theme implements each contract, name -> theme slug. */
247
+ readonly hosts?: Readonly<Record<string, string>>;
248
+ readonly modules: ReadonlyArray<UiModuleEntryManifest>;
249
+ }
250
+ export interface UiModuleEntryManifest {
251
+ readonly module: string;
252
+ readonly contract: string;
253
+ readonly range: string;
254
+ readonly framework: string;
255
+ }
256
+ export interface ApiManifest {
257
+ readonly apiBase: string;
258
+ readonly ui?: UiApiManifest;
259
+ readonly configBase?: string;
260
+ readonly auth?: AuthApiManifest;
261
+ readonly identity?: IdentityApiManifest;
262
+ readonly sections?: SectionApiManifest;
263
+ readonly navi?: NaviApiManifest;
264
+ readonly content?: ContentApiManifest;
265
+ readonly dataGrid?: DataGridApiManifest;
266
+ readonly terminal?: TerminalApiManifest;
267
+ readonly media?: MediaApiManifest;
268
+ readonly document?: DocumentApiManifest;
269
+ readonly vfs?: VfsApiManifest;
270
+ readonly dynamicEntity?: DynamicEntityApiManifest;
271
+ readonly domainExplorer?: DomainExplorerApiManifest;
272
+ readonly editor?: EditorApiManifest;
273
+ readonly viewers?: ViewerApiManifest;
274
+ readonly supportedLocales?: Array<{
275
+ code: string;
276
+ label: string;
277
+ }>;
278
+ readonly platformDefaults?: PlatformDefaults;
281
279
  }
282
280
  /**
283
281
  * GET /api/v1/admin/manifest -- the admin's endpoint map, answered to every signed-in account
@@ -285,15 +283,15 @@ interface ApiManifest {
285
283
  * from before that answers at /api/v1/console/manifest, to the console group only. Read once
286
284
  * signed in.
287
285
  */
288
- interface ConsoleManifestResponse {
289
- readonly manifest: ApiManifest;
286
+ export interface ConsoleManifestResponse {
287
+ readonly manifest: ApiManifest;
290
288
  }
291
- interface ThemeConfigResponse {
292
- readonly slug?: string | null;
293
- readonly feStack?: string | null;
294
- readonly spaFramework?: string | null;
295
- readonly assetsUrl?: string | null;
296
- readonly manifest: ApiManifest;
289
+ export interface ThemeConfigResponse {
290
+ readonly slug?: string | null;
291
+ readonly feStack?: string | null;
292
+ readonly spaFramework?: string | null;
293
+ readonly assetsUrl?: string | null;
294
+ readonly manifest: ApiManifest;
297
295
  }
298
296
  /**
299
297
  * Replaces {param} tokens in a URL pattern with encoded values.
@@ -302,8 +300,7 @@ interface ThemeConfigResponse {
302
300
  * resolvePattern(manifest.sections!.item, { id: '123' })
303
301
  * // -> '/api/v1/sections/123'
304
302
  */
305
- declare function resolvePattern(pattern: string, params: Record<string, string>): string;
306
-
303
+ export declare function resolvePattern(pattern: string, params: Record<string, string>): string;
307
304
  /**
308
305
  * Shapes the user-preference store persists.
309
306
  *
@@ -316,12 +313,11 @@ declare function resolvePattern(pattern: string, params: Record<string, string>)
316
313
  * keeps its own local path and nothing that reads a grid preference had to
317
314
  * change.
318
315
  */
319
- interface DataGridPreference {
320
- columns?: string[];
321
- columnWidths?: Record<string, number>;
316
+ export interface DataGridPreference {
317
+ columns?: string[];
318
+ columnWidths?: Record<string, number>;
322
319
  }
323
- type DataGridPreferences = Record<string, DataGridPreference>;
324
-
320
+ export type DataGridPreferences = Record<string, DataGridPreference>;
325
321
  /**
326
322
  * Session and current-user DTOs.
327
323
  *
@@ -333,123 +329,121 @@ type DataGridPreferences = Record<string, DataGridPreference>;
333
329
  * `ApiService` re-exports them from its own module, so the feature files that
334
330
  * import `UserDto` from there are untouched.
335
331
  */
336
- interface TokenResponse {
337
- accessToken: string;
338
- refreshToken: string;
339
- expiresAt: string;
340
- user?: UserDto;
341
- }
342
- interface UserDto {
343
- id: string;
344
- email: string;
345
- identifier?: string;
346
- roles: string[];
347
- uiPrefs?: DataGridPreferences;
348
- avatarUrl?: string | null;
349
- firstName?: string | null;
350
- lastName?: string | null;
351
- fullName?: string;
352
- }
353
-
354
- type FieldType = 'text' | 'email' | 'password' | 'number' | 'textarea' | 'select' | 'toggle' | 'date' | 'time' | 'hidden' | 'relation' | 'subform' | 'token-pattern' | 'richtext' | 'localizedText' | 'localizedTextarea' | 'optionsEditor' | 'intlPhone' | 'color';
355
- type DataSourceType = 'static' | 'enum' | 'api' | 'repo';
356
- type VisibilityOperator = 'eq' | 'ne' | 'in' | 'notIn' | 'empty' | 'notEmpty';
357
- interface VisibilityCondition {
358
- readonly field: string;
359
- readonly operator: VisibilityOperator;
360
- readonly value?: unknown;
361
- }
362
- type DataSourceWidget = 'select' | 'select-search' | 'select-tree' | 'token-input' | 'media-picker';
363
- type DataSourceLoading = 'eager' | 'lazy';
364
- interface DataSourceOption {
365
- readonly value: unknown;
366
- readonly label: string;
367
- readonly parentId?: string | null;
368
- }
369
- interface DataSourceDefinition {
370
- readonly type: DataSourceType;
371
- readonly bindValue: string;
372
- readonly bindLabel: string;
373
- readonly multiple: boolean;
374
- readonly maxItems?: number;
375
- readonly options?: ReadonlyArray<DataSourceOption>;
376
- readonly url?: string;
377
- readonly widget: DataSourceWidget;
378
- readonly loading: DataSourceLoading;
379
- /** Free-form widget-specific configuration block populated from YAML
380
- * `dataSource.widgetOptions`. Used by media-picker for bindTarget /
381
- * display / accept / recentlyUsed / hoverPreview, and reserved for
382
- * future custom widgets. Kept separate from `options` (which is the
383
- * static-option list) to avoid key collisions. */
384
- readonly widgetOptions?: Readonly<Record<string, unknown>>;
385
- }
386
- interface ValidatorDefinition {
387
- readonly type: string;
388
- readonly value?: unknown;
389
- readonly message?: string;
390
- }
391
- interface RelationDefinition {
392
- readonly cardinality: 'one' | 'many';
393
- readonly maxItems?: number;
394
- readonly targetFormId?: string;
395
- readonly dataSource?: DataSourceDefinition;
396
- }
397
- interface SubFormDefinition {
398
- readonly formId: string;
399
- readonly relation: 'one' | 'many';
400
- readonly maxItems?: number;
401
- }
402
- interface FieldSecurityPolicy {
403
- readonly read: ReadonlyArray<string>;
404
- readonly write: ReadonlyArray<string>;
405
- }
406
- interface FieldItem {
407
- readonly alias: string;
408
- readonly type: FieldType;
409
- readonly label: string;
410
- readonly placeholder?: string;
411
- readonly hint?: string;
412
- readonly required: boolean;
413
- readonly readonly: boolean;
414
- readonly locked: boolean;
415
- readonly private: boolean;
416
- readonly validators: ReadonlyArray<ValidatorDefinition>;
417
- readonly dataSource?: DataSourceDefinition;
418
- readonly relation?: RelationDefinition;
419
- readonly subForm?: SubFormDefinition;
420
- readonly separators?: ReadonlyArray<string>;
421
- readonly security?: FieldSecurityPolicy | null;
422
- readonly autocomplete?: string;
423
- }
424
- interface LayoutNode {
425
- readonly type: 'group' | 'grid' | 'column' | 'tabs' | 'tab' | (string & {});
426
- readonly name?: string;
427
- readonly description?: string;
428
- readonly compact?: boolean;
429
- readonly width?: number;
430
- readonly children?: ReadonlyArray<LayoutNode>;
431
- readonly showWhen?: VisibilityCondition;
432
- }
433
- type FormActionType = 'submit' | 'button' | 'reset';
434
- type FormActionVariant = 'primary' | 'secondary' | 'danger' | 'link';
332
+ export interface TokenResponse {
333
+ accessToken: string;
334
+ refreshToken: string;
335
+ expiresAt: string;
336
+ user?: UserDto;
337
+ }
338
+ export interface UserDto {
339
+ id: string;
340
+ email: string;
341
+ identifier?: string;
342
+ roles: string[];
343
+ uiPrefs?: DataGridPreferences;
344
+ avatarUrl?: string | null;
345
+ firstName?: string | null;
346
+ lastName?: string | null;
347
+ fullName?: string;
348
+ }
349
+ export type FieldType = 'text' | 'email' | 'password' | 'number' | 'textarea' | 'select' | 'toggle' | 'date' | 'time' | 'hidden' | 'relation' | 'subform' | 'token-pattern' | 'richtext' | 'localizedText' | 'localizedTextarea' | 'optionsEditor' | 'intlPhone' | 'color';
350
+ export type DataSourceType = 'static' | 'enum' | 'api' | 'repo';
351
+ export type VisibilityOperator = 'eq' | 'ne' | 'in' | 'notIn' | 'empty' | 'notEmpty';
352
+ export interface VisibilityCondition {
353
+ readonly field: string;
354
+ readonly operator: VisibilityOperator;
355
+ readonly value?: unknown;
356
+ }
357
+ export type DataSourceWidget = 'select' | 'select-search' | 'select-tree' | 'token-input' | 'media-picker';
358
+ export type DataSourceLoading = 'eager' | 'lazy';
359
+ export interface DataSourceOption {
360
+ readonly value: unknown;
361
+ readonly label: string;
362
+ readonly parentId?: string | null;
363
+ }
364
+ export interface DataSourceDefinition {
365
+ readonly type: DataSourceType;
366
+ readonly bindValue: string;
367
+ readonly bindLabel: string;
368
+ readonly multiple: boolean;
369
+ readonly maxItems?: number;
370
+ readonly options?: ReadonlyArray<DataSourceOption>;
371
+ readonly url?: string;
372
+ readonly widget: DataSourceWidget;
373
+ readonly loading: DataSourceLoading;
374
+ /** Free-form widget-specific configuration block populated from YAML
375
+ * `dataSource.widgetOptions`. Used by media-picker for bindTarget /
376
+ * display / accept / recentlyUsed / hoverPreview, and reserved for
377
+ * future custom widgets. Kept separate from `options` (which is the
378
+ * static-option list) to avoid key collisions. */
379
+ readonly widgetOptions?: Readonly<Record<string, unknown>>;
380
+ }
381
+ export interface ValidatorDefinition {
382
+ readonly type: string;
383
+ readonly value?: unknown;
384
+ readonly message?: string;
385
+ }
386
+ export interface RelationDefinition {
387
+ readonly cardinality: 'one' | 'many';
388
+ readonly maxItems?: number;
389
+ readonly targetFormId?: string;
390
+ readonly dataSource?: DataSourceDefinition;
391
+ }
392
+ export interface SubFormDefinition {
393
+ readonly formId: string;
394
+ readonly relation: 'one' | 'many';
395
+ readonly maxItems?: number;
396
+ }
397
+ export interface FieldSecurityPolicy {
398
+ readonly read: ReadonlyArray<string>;
399
+ readonly write: ReadonlyArray<string>;
400
+ }
401
+ export interface FieldItem {
402
+ readonly alias: string;
403
+ readonly type: FieldType;
404
+ readonly label: string;
405
+ readonly placeholder?: string;
406
+ readonly hint?: string;
407
+ readonly required: boolean;
408
+ readonly readonly: boolean;
409
+ readonly locked: boolean;
410
+ readonly private: boolean;
411
+ readonly validators: ReadonlyArray<ValidatorDefinition>;
412
+ readonly dataSource?: DataSourceDefinition;
413
+ readonly relation?: RelationDefinition;
414
+ readonly subForm?: SubFormDefinition;
415
+ readonly separators?: ReadonlyArray<string>;
416
+ readonly security?: FieldSecurityPolicy | null;
417
+ readonly autocomplete?: string;
418
+ }
419
+ export interface LayoutNode {
420
+ readonly type: 'group' | 'grid' | 'column' | 'tabs' | 'tab' | (string & {});
421
+ readonly name?: string;
422
+ readonly description?: string;
423
+ readonly compact?: boolean;
424
+ readonly width?: number;
425
+ readonly children?: ReadonlyArray<LayoutNode>;
426
+ readonly showWhen?: VisibilityCondition;
427
+ }
428
+ export type FormActionType = 'submit' | 'button' | 'reset';
429
+ export type FormActionVariant = 'primary' | 'secondary' | 'danger' | 'link';
435
430
  /** Terminal form button (Submit / Cancel / ...) -- mirrors PHP FormAction.
436
431
  * Resolved by the backend (declared `formOptions.actions`, else lifted from
437
432
  * legacy submit fields, else a default Submit). `label` is already translated. */
438
- interface FormAction {
439
- readonly type: FormActionType;
440
- readonly label: string;
441
- readonly name: string;
442
- readonly variant: FormActionVariant;
443
- }
444
- interface FormRenderDefinition {
445
- readonly id: string;
446
- readonly context: 'create' | 'edit';
447
- readonly layout: ReadonlyArray<LayoutNode>;
448
- readonly items: ReadonlyArray<FieldItem>;
449
- readonly actions: ReadonlyArray<FormAction>;
450
- readonly action?: string;
451
- }
452
-
433
+ export interface FormAction {
434
+ readonly type: FormActionType;
435
+ readonly label: string;
436
+ readonly name: string;
437
+ readonly variant: FormActionVariant;
438
+ }
439
+ export interface FormRenderDefinition {
440
+ readonly id: string;
441
+ readonly context: 'create' | 'edit';
442
+ readonly layout: ReadonlyArray<LayoutNode>;
443
+ readonly items: ReadonlyArray<FieldItem>;
444
+ readonly actions: ReadonlyArray<FormAction>;
445
+ readonly action?: string;
446
+ }
453
447
  /**
454
448
  * The collection envelope API Platform wraps every list response in.
455
449
  *
@@ -470,18 +464,17 @@ interface FormRenderDefinition {
470
464
  * crashes if it does. Settle it by calling the endpoint, not by merging the
471
465
  * types.
472
466
  */
473
- interface HydraView {
474
- '@id': string;
475
- 'next'?: string;
476
- 'previous'?: string;
477
- 'last'?: string;
478
- }
479
- interface HydraCollection<T> {
480
- 'member': T[];
481
- 'totalItems': number;
482
- 'view'?: HydraView;
483
- }
484
-
467
+ export interface HydraView {
468
+ '@id': string;
469
+ 'next'?: string;
470
+ 'previous'?: string;
471
+ 'last'?: string;
472
+ }
473
+ export interface HydraCollection<T> {
474
+ 'member': T[];
475
+ 'totalItems': number;
476
+ 'view'?: HydraView;
477
+ }
485
478
  /**
486
479
  * The identity endpoints core owns: the session lifecycle, plus the settings
487
480
  * read at boot.
@@ -498,47 +491,46 @@ interface HydraCollection<T> {
498
491
  * `ApiService` keeps its own identical signatures and delegates here, so none
499
492
  * of the 94 feature files calling it changed.
500
493
  */
501
- declare class IdentityApiClient {
502
- private readonly http;
503
- private readonly store;
504
- /** URLs come from the boot manifest, which core already owns. */
505
- private get manifest();
506
- login(identifier: string, password: string): Observable<TokenResponse>;
507
- refresh(refreshToken: string): Observable<TokenResponse>;
508
- logout(): Observable<void>;
509
- me(): Observable<UserDto>;
510
- getSettings(): Observable<Record<string, Record<string, unknown>>>;
511
- static ɵfac: i0.ɵɵFactoryDeclaration<IdentityApiClient, never>;
512
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
513
- }
514
-
494
+ export declare class IdentityApiClient {
495
+ private readonly http;
496
+ private readonly store;
497
+ /** URLs come from the boot manifest, which core already owns. */
498
+ private get manifest();
499
+ login(identifier: string, password: string): Observable<TokenResponse>;
500
+ refresh(refreshToken: string): Observable<TokenResponse>;
501
+ logout(): Observable<void>;
502
+ me(): Observable<UserDto>;
503
+ getSettings(): Observable<Record<string, Record<string, unknown>>>;
504
+ static ɵfac: i0.ɵɵFactoryDeclaration<IdentityApiClient, never>;
505
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
506
+ }
515
507
  /**
516
508
  * Response of `POST /centrifugo/connection-token`: the JWT the realtime SDK
517
509
  * opens its socket with, and what it needs to refresh before expiry.
518
510
  */
519
- interface CentrifugoConnectionTokenDto {
520
- token: string;
521
- expiresAt: number;
522
- ttl: number;
523
- wsUrl: string;
524
- /**
525
- * The code the server closes this connection with when the caller's session
526
- * ended (a sign-out everywhere, a password change, a deactivation). On it the
527
- * client signs out and ends its calls at once, without reconnecting or waiting
528
- * for a 401 (2026-09-26). Absent from a server older than that.
529
- */
530
- sessionEndedCode?: number;
511
+ export interface CentrifugoConnectionTokenDto {
512
+ token: string;
513
+ expiresAt: number;
514
+ ttl: number;
515
+ wsUrl: string;
516
+ /**
517
+ * The code the server closes this connection with when the caller's session
518
+ * ended (a sign-out everywhere, a password change, a deactivation). On it the
519
+ * client signs out and ends its calls at once, without reconnecting or waiting
520
+ * for a 401 (2026-09-26). Absent from a server older than that.
521
+ */
522
+ sessionEndedCode?: number;
531
523
  }
532
524
  /**
533
525
  * Response of `POST /centrifugo/subscription-token`: a per-channel JWT. The
534
526
  * SDK asks for one before subscribing to a private namespace, so this is
535
527
  * fetched per subscription rather than once per connection.
536
528
  */
537
- interface CentrifugoSubscriptionTokenDto {
538
- channel: string;
539
- token: string;
540
- expiresAt: number;
541
- ttl: number;
529
+ export interface CentrifugoSubscriptionTokenDto {
530
+ channel: string;
531
+ token: string;
532
+ expiresAt: number;
533
+ ttl: number;
542
534
  }
543
535
  /**
544
536
  * The two realtime endpoints, carved out for the same reason the identity ones
@@ -569,73 +561,70 @@ interface CentrifugoSubscriptionTokenDto {
569
561
  * channel. The wait stays for a server from before that, where a refused sign-in still opens
570
562
  * no socket.
571
563
  */
572
- declare class RealtimeTokenClient {
573
- /** The error a refused sign-in's connection-token request ends with. */
574
- static readonly REFUSED = "no console access: no realtime connection";
575
- private readonly http;
576
- private readonly store;
577
- private readonly injector;
578
- private get manifest();
579
- connectionToken(): Observable<CentrifugoConnectionTokenDto>;
580
- subscriptionToken(channel: string): Observable<CentrifugoSubscriptionTokenDto>;
581
- static ɵfac: i0.ɵɵFactoryDeclaration<RealtimeTokenClient, never>;
582
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
583
- }
584
-
585
- declare class Login {
586
- identifier: string;
587
- password: string;
588
- static readonly type = "[Auth] Login";
589
- constructor(identifier: string, password: string);
590
- }
591
- declare class Logout {
592
- static readonly type = "[Auth] Logout";
593
- }
594
- declare class RestoreSession {
595
- static readonly type = "[Auth] Restore Session";
596
- }
597
- declare class SetTokens {
598
- readonly response: TokenResponse;
599
- static readonly type = "[Auth] Set Tokens";
600
- constructor(response: TokenResponse);
564
+ export declare class RealtimeTokenClient {
565
+ /** The error a refused sign-in's connection-token request ends with. */
566
+ static readonly REFUSED = "no console access: no realtime connection";
567
+ private readonly http;
568
+ private readonly store;
569
+ private readonly injector;
570
+ private get manifest();
571
+ connectionToken(): Observable<CentrifugoConnectionTokenDto>;
572
+ subscriptionToken(channel: string): Observable<CentrifugoSubscriptionTokenDto>;
573
+ static ɵfac: i0.ɵɵFactoryDeclaration<RealtimeTokenClient, never>;
574
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
575
+ }
576
+ export declare class Login {
577
+ identifier: string;
578
+ password: string;
579
+ static readonly type = "[Auth] Login";
580
+ constructor(identifier: string, password: string);
581
+ }
582
+ export declare class Logout {
583
+ static readonly type = "[Auth] Logout";
584
+ }
585
+ export declare class RestoreSession {
586
+ static readonly type = "[Auth] Restore Session";
587
+ }
588
+ export declare class SetTokens {
589
+ readonly response: TokenResponse;
590
+ static readonly type = "[Auth] Set Tokens";
591
+ constructor(response: TokenResponse);
601
592
  }
602
593
  /** Merge a partial update into the currently-stored user (e.g. after avatar change). */
603
- declare class PatchCurrentUser {
604
- readonly patch: Partial<UserDto>;
605
- static readonly type = "[Auth] Patch Current User";
606
- constructor(patch: Partial<UserDto>);
607
- }
608
-
609
- interface AuthStateModel {
610
- accessToken: string | null;
611
- refreshToken: string | null;
612
- expiresAt: string | null;
613
- user: UserDto | null;
614
- }
615
- declare class AuthState {
616
- private readonly api;
617
- private readonly prefs;
618
- login(ctx: StateContext<AuthStateModel>, { identifier, password }: Login): rxjs.Observable<UserDto | null>;
619
- logout(ctx: StateContext<AuthStateModel>): void;
620
- restore(ctx: StateContext<AuthStateModel>): void;
621
- setTokens(ctx: StateContext<AuthStateModel>, { response }: SetTokens): void;
622
- patchCurrentUser(ctx: StateContext<AuthStateModel>, { patch }: PatchCurrentUser): void;
623
- static isAuthenticated(state: AuthStateModel): boolean;
624
- static accessToken(state: AuthStateModel): string | null;
625
- static refreshToken(state: AuthStateModel): string | null;
626
- /**
627
- * Sub-phase Y -- expose the access-token expiry timestamp so
628
- * preemptive-refresh callers (e.g., Centrifuge getToken
629
- * callbacks) can decide whether to rotate before issuing the
630
- * next protected request, instead of relying on the
631
- * interceptor's reactive 401-retry path.
632
- */
633
- static expiresAt(state: AuthStateModel): string | null;
634
- static currentUser(state: AuthStateModel): UserDto | null;
635
- static ɵfac: i0.ɵɵFactoryDeclaration<AuthState, never>;
636
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
637
- }
638
-
594
+ export declare class PatchCurrentUser {
595
+ readonly patch: Partial<UserDto>;
596
+ static readonly type = "[Auth] Patch Current User";
597
+ constructor(patch: Partial<UserDto>);
598
+ }
599
+ export interface AuthStateModel {
600
+ accessToken: string | null;
601
+ refreshToken: string | null;
602
+ expiresAt: string | null;
603
+ user: UserDto | null;
604
+ }
605
+ export declare class AuthState {
606
+ private readonly api;
607
+ private readonly prefs;
608
+ login(ctx: StateContext<AuthStateModel>, { identifier, password }: Login): import("rxjs").Observable<UserDto | null>;
609
+ logout(ctx: StateContext<AuthStateModel>): void;
610
+ restore(ctx: StateContext<AuthStateModel>): void;
611
+ setTokens(ctx: StateContext<AuthStateModel>, { response }: SetTokens): void;
612
+ patchCurrentUser(ctx: StateContext<AuthStateModel>, { patch }: PatchCurrentUser): void;
613
+ static isAuthenticated(state: AuthStateModel): boolean;
614
+ static accessToken(state: AuthStateModel): string | null;
615
+ static refreshToken(state: AuthStateModel): string | null;
616
+ /**
617
+ * Sub-phase Y -- expose the access-token expiry timestamp so
618
+ * preemptive-refresh callers (e.g., Centrifuge getToken
619
+ * callbacks) can decide whether to rotate before issuing the
620
+ * next protected request, instead of relying on the
621
+ * interceptor's reactive 401-retry path.
622
+ */
623
+ static expiresAt(state: AuthStateModel): string | null;
624
+ static currentUser(state: AuthStateModel): UserDto | null;
625
+ static ɵfac: i0.ɵɵFactoryDeclaration<AuthState, never>;
626
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
627
+ }
639
628
  /**
640
629
  * Protects routes that require authentication.
641
630
  *
@@ -652,8 +641,7 @@ declare class AuthState {
652
641
  * sent to the login page, which says why. A failure to ask is not a refusal: the console opens, and every request it
653
642
  * makes is still decided by the server.
654
643
  */
655
- declare const authGuard: CanActivateFn;
656
-
644
+ export declare const authGuard: CanActivateFn;
657
645
  /**
658
646
  * Guards the `/login` route -- redirects already-authenticated
659
647
  * callers to admin home so a stale tab parked on `/login` cannot
@@ -669,11 +657,9 @@ declare const authGuard: CanActivateFn;
669
657
  * as anonymous, allow `/login`, and only then have the token
670
658
  * populated.
671
659
  */
672
- declare const loginPageGuard: CanActivateFn;
673
-
674
- declare const BYPASS_AUTH: HttpContextToken<boolean>;
675
- declare const authInterceptor: HttpInterceptorFn;
676
-
660
+ export declare const loginPageGuard: CanActivateFn;
661
+ export declare const BYPASS_AUTH: HttpContextToken<boolean>;
662
+ export declare const authInterceptor: HttpInterceptorFn;
677
663
  /**
678
664
  * Two-layer refresh coordinator: an intra-tab `shareReplay(1)`
679
665
  * mutex dedupes concurrent callers within one tab, and a
@@ -718,63 +704,62 @@ declare const authInterceptor: HttpInterceptorFn;
718
704
  * made is never replayed with the token it consumed. A 401 there
719
705
  * is the session's end: `Logout`.
720
706
  */
721
- declare class AuthRefreshCoordinator {
722
- private readonly api;
723
- private readonly store;
724
- private readonly router;
725
- private inFlight;
726
- /**
727
- * Return the in-flight refresh Observable, or spawn a new one
728
- * with the supplied refresh-token value.
729
- *
730
- * `refusedAccessToken`: the access token the server just answered
731
- * 401 to, when the caller is answering one. With it, the lock body
732
- * refreshes unless the shared copy already holds another token;
733
- * without it (a preemptive refresh), a fresh-looking cached token
734
- * is handed back as before.
735
- *
736
- * Callers subscribe via `switchMap` / `firstValueFrom` to retry
737
- * their original request with the resolved access token.
738
- */
739
- refresh(refreshToken: string, refusedAccessToken?: string | null): Observable<string>;
740
- /**
741
- * Run the refresh body inside `navigator.locks` when the API is
742
- * available; degrade to direct invocation otherwise. The lock
743
- * body checks for state already updated by a sibling tab before
744
- * issuing its own HTTP call.
745
- */
746
- private refreshWithCrossTabLock;
747
- /**
748
- * Lock body. Re-reads the shared copy first (see the class note).
749
- *
750
- * Answering a refusal: a shared access token other than the refused
751
- * one is a rotation already made -- taken, and mirrored into this
752
- * tab's state if its `storage` event has not arrived yet. Otherwise,
753
- * or for a preemptive caller whose cached token no longer looks
754
- * fresh, the HTTP refresh, with the newest refresh token: shared,
755
- * then state, then the caller's.
756
- */
757
- private executeRefresh;
758
- /**
759
- * The session as the tabs share it, or null when there is none to
760
- * read: no storage, nothing stored (signed out), or a value that is
761
- * not a complete token set.
762
- */
763
- private readShared;
764
- /**
765
- * For a preemptive caller only: return the current access token when
766
- * its expiry is more than `FRESH_TOKEN_GRACE_MS` milliseconds away --
767
- * signalling that some other tab refreshed for us. The shared copy
768
- * first, which a sibling's rotation reaches before this tab's state
769
- * does (mirrored here when it is newer); this tab's state when there
770
- * is none. Null otherwise. Never asked for a token the server
771
- * refused: a refused token's expiry looks just as fresh.
772
- */
773
- private tryConsumeFreshState;
774
- static ɵfac: i0.ɵɵFactoryDeclaration<AuthRefreshCoordinator, never>;
775
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
776
- }
777
-
707
+ export declare class AuthRefreshCoordinator {
708
+ private readonly api;
709
+ private readonly store;
710
+ private readonly router;
711
+ private inFlight;
712
+ /**
713
+ * Return the in-flight refresh Observable, or spawn a new one
714
+ * with the supplied refresh-token value.
715
+ *
716
+ * `refusedAccessToken`: the access token the server just answered
717
+ * 401 to, when the caller is answering one. With it, the lock body
718
+ * refreshes unless the shared copy already holds another token;
719
+ * without it (a preemptive refresh), a fresh-looking cached token
720
+ * is handed back as before.
721
+ *
722
+ * Callers subscribe via `switchMap` / `firstValueFrom` to retry
723
+ * their original request with the resolved access token.
724
+ */
725
+ refresh(refreshToken: string, refusedAccessToken?: string | null): Observable<string>;
726
+ /**
727
+ * Run the refresh body inside `navigator.locks` when the API is
728
+ * available; degrade to direct invocation otherwise. The lock
729
+ * body checks for state already updated by a sibling tab before
730
+ * issuing its own HTTP call.
731
+ */
732
+ private refreshWithCrossTabLock;
733
+ /**
734
+ * Lock body. Re-reads the shared copy first (see the class note).
735
+ *
736
+ * Answering a refusal: a shared access token other than the refused
737
+ * one is a rotation already made -- taken, and mirrored into this
738
+ * tab's state if its `storage` event has not arrived yet. Otherwise,
739
+ * or for a preemptive caller whose cached token no longer looks
740
+ * fresh, the HTTP refresh, with the newest refresh token: shared,
741
+ * then state, then the caller's.
742
+ */
743
+ private executeRefresh;
744
+ /**
745
+ * The session as the tabs share it, or null when there is none to
746
+ * read: no storage, nothing stored (signed out), or a value that is
747
+ * not a complete token set.
748
+ */
749
+ private readShared;
750
+ /**
751
+ * For a preemptive caller only: return the current access token when
752
+ * its expiry is more than `FRESH_TOKEN_GRACE_MS` milliseconds away --
753
+ * signalling that some other tab refreshed for us. The shared copy
754
+ * first, which a sibling's rotation reaches before this tab's state
755
+ * does (mirrored here when it is newer); this tab's state when there
756
+ * is none. Null otherwise. Never asked for a token the server
757
+ * refused: a refused token's expiry looks just as fresh.
758
+ */
759
+ private tryConsumeFreshState;
760
+ static ɵfac: i0.ɵɵFactoryDeclaration<AuthRefreshCoordinator, never>;
761
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
762
+ }
778
763
  /**
779
764
  * Bridges shared `localStorage` mutations into the NGXS auth
780
765
  * state so a refresh executed in one tab silently propagates to
@@ -799,15 +784,14 @@ declare class AuthRefreshCoordinator {
799
784
  * Started from `AppInitService.load()` so the listener is in
800
785
  * place before any protected HTTP fires.
801
786
  */
802
- declare class CrossTabAuthSyncService {
803
- private readonly store;
804
- private listenerAttached;
805
- start(): void;
806
- private readonly handleStorageEvent;
807
- static ɵfac: i0.ɵɵFactoryDeclaration<CrossTabAuthSyncService, never>;
808
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
809
- }
810
-
787
+ export declare class CrossTabAuthSyncService {
788
+ private readonly store;
789
+ private listenerAttached;
790
+ start(): void;
791
+ private readonly handleStorageEvent;
792
+ static ɵfac: i0.ɵɵFactoryDeclaration<CrossTabAuthSyncService, never>;
793
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
794
+ }
811
795
  /**
812
796
  * The elevation state of the signed-in session: the wire shape of
813
797
  * `GET /auth/elevation`, returned again by `POST` on a grant.
@@ -818,36 +802,41 @@ declare class CrossTabAuthSyncService {
818
802
  * capability flags are the two readings of ONE server-side decision, and the
819
803
  * client keeps no third.
820
804
  */
821
- interface ElevationState {
822
- /** Whether this session is elevated right now. */
823
- elevated: boolean;
824
- /** When the current elevation ends (ISO 8601); absent or null when not elevated. */
825
- expiresAt?: string | null;
826
- /** When the current elevation was granted (ISO 8601); absent or null when not elevated. */
827
- grantedAt?: string | null;
828
- /** How the LAST elevation of this session ended. Rendered by the prompt before it asks. */
829
- ended: ElevationEnded;
830
- /** The lifetime a grant made now would get, in seconds (the setting, clamped to the ceiling). */
831
- lifetimeSeconds: number;
832
- /** The most any elevation can last, fixed in code. */
833
- lifetimeCeilingSeconds: number;
834
- /** Whether the CURRENT elevation was confirmed by a second factor; absent or null when not elevated. */
835
- factorConfirmed?: boolean | null;
836
- /** Whether a grant made now must carry a code from the person's own authenticator. */
837
- mfaRequired: boolean;
838
- /** What the prompt shows before asking; two of the codes mean this account cannot elevate yet. */
839
- warnings: ElevationWarning[];
805
+ export interface ElevationState {
806
+ /** Whether this session is elevated right now. */
807
+ elevated: boolean;
808
+ /** When the current elevation ends (ISO 8601); absent or null when not elevated. */
809
+ expiresAt?: string | null;
810
+ /** When the current elevation was granted (ISO 8601); absent or null when not elevated. */
811
+ grantedAt?: string | null;
812
+ /** How the LAST elevation of this session ended. Rendered by the prompt before it asks. */
813
+ ended: ElevationEnded;
814
+ /** The lifetime a grant made now would get, in seconds (the setting, clamped to the ceiling). */
815
+ lifetimeSeconds: number;
816
+ /** The most any elevation can last, fixed in code. */
817
+ lifetimeCeilingSeconds: number;
818
+ /** Whether the CURRENT elevation was confirmed by a second factor; absent or null when not elevated. */
819
+ factorConfirmed?: boolean | null;
820
+ /** Whether a grant made now must carry a code from the person's own authenticator. */
821
+ mfaRequired: boolean;
822
+ /** What the prompt shows before asking; two of the codes mean this account cannot elevate yet. */
823
+ warnings: ElevationWarning[];
824
+ /**
825
+ * Whether this account may elevate at all -- a member of the admin group, elevated or not. The one source a
826
+ * client offers "Elevate session" from; absent (an older server) reads as false.
827
+ */
828
+ mayElevate?: boolean;
840
829
  }
841
830
  /**
842
831
  * `closed` is the beacon (the panel was closed or reloaded), `dropped_by_user`
843
832
  * the explicit drop, `refresh_from_other_pair` the server's refresh tripwire,
844
833
  * `never` a session that has not elevated yet.
845
834
  */
846
- type ElevationEndedReason = 'expired' | 'closed' | 'dropped_by_user' | 'refresh_from_other_pair' | 'never';
847
- interface ElevationEnded {
848
- reason: ElevationEndedReason;
849
- /** ISO 8601, null for `never`. */
850
- at: string | null;
835
+ export type ElevationEndedReason = 'expired' | 'closed' | 'dropped_by_user' | 'refresh_from_other_pair' | 'never';
836
+ export interface ElevationEnded {
837
+ reason: ElevationEndedReason;
838
+ /** ISO 8601, null for `never`. */
839
+ at: string | null;
851
840
  }
852
841
  /**
853
842
  * `elevation.password_not_set` and `elevation.factor_missing` mean this
@@ -855,23 +844,22 @@ interface ElevationEnded {
855
844
  * own) and the message says which;
856
845
  * `elevation.mfa_off` is the recommendation carried while the switch is off.
857
846
  */
858
- interface ElevationWarning {
859
- /** The three the server documents today; the client renders any it does not know as text. */
860
- code: string;
861
- message: string;
862
- }
863
- declare const ELEVATION_WARNING_PASSWORD_NOT_SET = "elevation.password_not_set";
864
- declare const ELEVATION_WARNING_FACTOR_MISSING = "elevation.factor_missing";
865
- declare const ELEVATION_WARNING_MFA_OFF = "elevation.mfa_off";
847
+ export interface ElevationWarning {
848
+ /** The three the server documents today; the client renders any it does not know as text. */
849
+ code: string;
850
+ message: string;
851
+ }
852
+ export declare const ELEVATION_WARNING_PASSWORD_NOT_SET = "elevation.password_not_set";
853
+ export declare const ELEVATION_WARNING_FACTOR_MISSING = "elevation.factor_missing";
854
+ export declare const ELEVATION_WARNING_MFA_OFF = "elevation.mfa_off";
866
855
  /** Why the elevation of a session changed, for whoever refetches on it. */
867
- type ElevationChangeKind = 'granted' | 'dropped' | 'expired' | 'observed';
868
- interface ElevationChange {
869
- kind: ElevationChangeKind;
870
- state: ElevationState;
856
+ export type ElevationChangeKind = 'granted' | 'dropped' | 'expired' | 'observed';
857
+ export interface ElevationChange {
858
+ kind: ElevationChangeKind;
859
+ state: ElevationState;
871
860
  }
872
861
  /** The two reasons the client sends when it drops an elevation itself. */
873
- type ElevationDropReason = 'dropped_by_user' | 'closed';
874
-
862
+ export type ElevationDropReason = 'dropped_by_user' | 'closed';
875
863
  /**
876
864
  * What the elevation prompt is told when it opens.
877
865
  *
@@ -885,9 +873,11 @@ type ElevationDropReason = 'dropped_by_user' | 'closed';
885
873
  * ("Permission denied: cannot write '/docs/...'"), when a 403 opened the
886
874
  * prompt; absent when a disabled control offered elevation instead.
887
875
  */
888
- interface ElevationPromptRequest {
889
- state: ElevationState;
890
- refusal?: string;
876
+ export interface ElevationPromptRequest {
877
+ state: ElevationState;
878
+ refusal?: string;
879
+ /** The refused action in words ("Saving navi nodes"), when a refused request opened the prompt. */
880
+ action?: string;
891
881
  }
892
882
  /**
893
883
  * The prompt itself is a dialog, and dialogs are the kit's -- a layer above
@@ -900,15 +890,15 @@ interface ElevationPromptRequest {
900
890
  * false when the person declined or could not. The port does not retry the
901
891
  * refused action; the interceptor does, on true.
902
892
  */
903
- interface ElevationPromptPort {
904
- open(request: ElevationPromptRequest): Observable<boolean>;
893
+ export interface ElevationPromptPort {
894
+ open(request: ElevationPromptRequest): Observable<boolean>;
905
895
  }
906
896
  /**
907
897
  * Optional by design: with nothing bound, a 403 stays a 403 and the state
908
898
  * service still answers -- an application without an admin panel has no
909
899
  * prompt to show and loses nothing.
910
900
  */
911
- declare const ELEVATION_PROMPT: InjectionToken<ElevationPromptPort>;
901
+ export declare const ELEVATION_PROMPT: InjectionToken<ElevationPromptPort>;
912
902
  /**
913
903
  * Told when an action a person took was refused for want of elevation: a
914
904
  * write (not a read, not a background request) answered by a stamped 403.
@@ -922,18 +912,24 @@ declare const ELEVATION_PROMPT: InjectionToken<ElevationPromptPort>;
922
912
  * `refusal` is the server's sentence for the refused action, already made
923
913
  * readable by the error service.
924
914
  */
925
- interface ElevationNoticePort {
926
- refused(refusal: string): void;
915
+ export interface ElevationNoticePort {
916
+ /** `action` names what the person did ("Saving navi nodes"); absent from older callers. */
917
+ refused(refusal: string, action?: string): void;
927
918
  }
919
+ /**
920
+ * What a write IS, in the person's words, for the elevation prompt and notice when the server refuses it -- set by a
921
+ * caller that knows better than the URL ("Saving the menu entry"). Without it the interceptor names the method's verb
922
+ * and the resource the URL names.
923
+ */
924
+ export declare const ELEVATION_ACTION: HttpContextToken<string | null>;
928
925
  /** Optional, like {@link ELEVATION_PROMPT}: with nothing bound, a refusal is only a 403. */
929
- declare const ELEVATION_NOTICE: InjectionToken<ElevationNoticePort>;
930
-
926
+ export declare const ELEVATION_NOTICE: InjectionToken<ElevationNoticePort>;
931
927
  /**
932
928
  * Set on a request that must never open the elevation prompt: the elevation
933
929
  * endpoint's own calls (a wrong password is a 403 and is not a refused action),
934
930
  * and any caller that would rather receive its 403 and decide for itself.
935
931
  */
936
- declare const BYPASS_ELEVATION: HttpContextToken<boolean>;
932
+ export declare const BYPASS_ELEVATION: HttpContextToken<boolean>;
937
933
  /**
938
934
  * The elevation state of this session, read from ONE place: the server.
939
935
  *
@@ -969,247 +965,262 @@ declare const BYPASS_ELEVATION: HttpContextToken<boolean>;
969
965
  * whose refusal came from the VFS permission service under another module's
970
966
  * path (media, document and word templates, content distribution).
971
967
  */
972
- declare const ELEVATION_REQUIRED_HEADER = "X-Elevation-Required";
968
+ export declare const ELEVATION_REQUIRED_HEADER = "X-Elevation-Required";
973
969
  /**
974
970
  * Set on a request no person asked for: a realtime token, a preferences sync, a
975
971
  * poll. Its refusal is never announced -- not by a dialog and not by a notice --
976
972
  * because nobody is waiting on it and nobody can act on it. The caller still
977
973
  * receives the 403 and decides what its own state shows.
978
974
  */
979
- declare const BACKGROUND_REQUEST: HttpContextToken<boolean>;
975
+ export declare const BACKGROUND_REQUEST: HttpContextToken<boolean>;
980
976
  /**
981
977
  * Whether a failed request was refused for want of elevation: a 403 carrying
982
978
  * the server's stamp. The one test every reader of a refusal uses -- the
983
979
  * interceptor, a page's inline state, the error text -- so they cannot disagree.
984
980
  */
985
- declare function isElevationRefusal(err: unknown): boolean;
986
- declare class ElevationService {
987
- private readonly http;
988
- private readonly store;
989
- private readonly prompt;
990
- private readonly destroyRef;
991
- /** The last answer the server gave, exactly as it gave it. */
992
- private readonly served;
993
- /**
994
- * The state as it stands NOW; null until the first read from the server.
995
- *
996
- * Deliberately NOT a `computed`. A cached derivation is only recomputed
997
- * when one of its dependencies changes, and nothing changes when an
998
- * elevation simply runs out -- so it would keep answering `elevated: true`
999
- * past the moment the server itself named, for as long as the machine
1000
- * happened to be asleep.
1001
- *
1002
- * Reading past `expiresAt` also demotes the STORED answer. The value
1003
- * returned here is already correct without that write; the write is only
1004
- * how cached readers (a `computed`, an OnPush template) find out.
1005
- */
1006
- readonly state: () => ElevationState | null;
1007
- /** Convenience over `state`: false until the server has said otherwise. */
1008
- readonly elevated: () => boolean;
1009
- /** `expiresAt` as a Date, null when not elevated. */
1010
- readonly expiresAt: () => Date | null;
1011
- /**
1012
- * Every change of elevation state: a grant, a drop (by this person, by the
1013
- * beacon, by the tripwire), an expiry, or an observed change of a live
1014
- * grant's `expiresAt`. A refresh that reports the same state emits nothing,
1015
- * so subscribers that refetch a listing on it never refetch for no reason.
1016
- */
1017
- readonly changes$: Subject<ElevationChange>;
1018
- private expiryTimer;
1019
- private pendingPrompt;
1020
- private expiryAnnouncePending;
1021
- private lastReconcileAt;
1022
- constructor();
1023
- /** The manifest carries the URL; without it the service is inert, not broken. */
1024
- get available(): boolean;
1025
- private get url();
1026
- /**
1027
- * Whether a URL belongs to THIS API: a relative one under the manifest's
1028
- * `apiBase`, or an absolute one on this origin under it. The interceptor
1029
- * acts on a stamped refusal from here and from nowhere else -- a third
1030
- * party answering 403 with a header of the same name is not an invitation
1031
- * to elevate this session.
1032
- */
1033
- isThisApi(url: string): boolean;
1034
- /** Ask the server. Applies the answer and announces it when it differs. */
1035
- refresh(): Observable<ElevationState>;
1036
- /**
1037
- * Elevate with the person's own password and, when the switch is on, a
1038
- * code from their own authenticator. Errors are the dialog's to branch on:
1039
- * 403 not a member of the admin group, wrong password, or wrong / replayed
1040
- * code, 428 a code is required, 409 this account cannot elevate yet, 429
1041
- * throttled. They pass through
1042
- * untouched, and never open the prompt (BYPASS_ELEVATION).
1043
- */
1044
- elevate(password: string, code?: string): Observable<ElevationState>;
1045
- /** Drop the elevation now. Idempotent on the server: nothing live is still a 204. */
1046
- drop(reason?: ElevationDropReason): Observable<ElevationState>;
1047
- /**
1048
- * What a person's explicit request for elevation calls -- an Elevate button,
1049
- * or a control that already knows its action needs elevation: ask the
1050
- * server whether this session is elevated, and only when it is not, open
1051
- * the prompt. Nothing calls it on a response by itself (the interceptor
1052
- * only tells {@link ELEVATION_NOTICE}). Elevated already is false, no
1053
- * prompt: elevation would change nothing.
1054
- */
1055
- offerFor(refusal?: string): Observable<boolean>;
1056
- /**
1057
- * Open the prompt, or join the one already open. Resolves to whether the
1058
- * session is elevated once the dialog closes.
1059
- */
1060
- requestElevation(request: ElevationPromptRequest): Observable<boolean>;
1061
- private options;
1062
- private inert;
1063
- /**
1064
- * Store the answer, schedule the expiry read, announce a change.
1065
- *
1066
- * `explicit` names the change when the caller knows it (a grant). For a
1067
- * plain refresh the kind is derived from the two states: elevated -> not
1068
- * is `expired` when the server says so and `dropped` otherwise (the person,
1069
- * another tab, the beacon, the tripwire); not -> elevated is a grant seen
1070
- * from another tab; a live grant whose `expiresAt` moved is `observed`.
1071
- *
1072
- * `prev` is read RAW rather than through `state()`: the demotion is not
1073
- * wanted here (this answer supersedes the stored one either way), and a
1074
- * read that demoted would queue an expiry announcement the server's own
1075
- * answer is about to make.
1076
- */
1077
- private apply;
1078
- private kindOf;
1079
- /**
1080
- * Tell the listings that this session's elevation ran out, once, and NOT
1081
- * from inside the read that noticed: a subscriber refetches on this, and a
1082
- * refetch started in the middle of a change detection pass is a write
1083
- * during a read. The microtask runs at the end of the current task, before
1084
- * anything can paint.
1085
- *
1086
- * A server answer arriving first clears the flag, so the announcement is
1087
- * made by whichever of the two got there first, never by both. Other tabs
1088
- * are NOT told: each one reaches the same conclusion from the same
1089
- * `expiresAt` on its own read, so the storage write would only buy a
1090
- * thundering herd of GETs.
1091
- */
1092
- private announceExpiryLater;
1093
- /**
1094
- * Ask the server again at `expiresAt` -- an OPTIMISATION, not the
1095
- * mechanism. `state()` already reports an elapsed grant as expired without
1096
- * being told, so a timer that a sleeping machine swallows, or that a
1097
- * background tab throttles to once a minute, now costs freshness rather
1098
- * than correctness. What it buys is the listing refetch happening at the
1099
- * moment of expiry instead of at the next read.
1100
- *
1101
- * Do not flip a local flag here: the lifetime counts from the grant on the
1102
- * server's clock, and if the server still says elevated at that moment
1103
- * (skew), the honest thing is to ask again shortly.
1104
- */
1105
- private schedule;
1106
- /**
1107
- * One more attempt, then stop. Giving up is affordable now and was not
1108
- * before: the state a failed read leaves behind is still read through the
1109
- * arithmetic above, so an unreachable server cannot leave this tab
1110
- * believing it is elevated. What is lost is only news from elsewhere -- a
1111
- * grant or a drop in another session -- and that is what the tab picks up
1112
- * when it next comes into view.
1113
- */
1114
- private scheduleRetry;
1115
- private clearTimer;
1116
- private announceToOtherTabs;
1117
- private readonly onStorage;
1118
- private readonly onFocus;
1119
- private readonly onVisibilityChange;
1120
- /**
1121
- * The tab is back. Read first -- that alone ends an elevation that ran out
1122
- * while it was away, with no request and no network to depend on -- then
1123
- * ask the server for what the read cannot know: a grant or a drop made
1124
- * elsewhere, or a lifetime the server moved.
1125
- */
1126
- private reconcile;
1127
- static ɵfac: i0.ɵɵFactoryDeclaration<ElevationService, never>;
1128
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1129
- }
1130
-
981
+ export declare function isElevationRefusal(err: unknown): boolean;
982
+ export declare class ElevationService {
983
+ private readonly http;
984
+ private readonly store;
985
+ private readonly prompt;
986
+ private readonly destroyRef;
987
+ /** The last answer the server gave, exactly as it gave it. */
988
+ private readonly served;
989
+ /**
990
+ * The state as it stands NOW; null until the first read from the server.
991
+ *
992
+ * Deliberately NOT a `computed`. A cached derivation is only recomputed
993
+ * when one of its dependencies changes, and nothing changes when an
994
+ * elevation simply runs out -- so it would keep answering `elevated: true`
995
+ * past the moment the server itself named, for as long as the machine
996
+ * happened to be asleep.
997
+ *
998
+ * Reading past `expiresAt` also demotes the STORED answer. The value
999
+ * returned here is already correct without that write; the write is only
1000
+ * how cached readers (a `computed`, an OnPush template) find out.
1001
+ */
1002
+ readonly state: () => ElevationState | null;
1003
+ /** Convenience over `state`: false until the server has said otherwise. */
1004
+ readonly elevated: () => boolean;
1005
+ /** Whether this account may elevate at all, as the server answered it: what "Elevate session" is offered from. */
1006
+ readonly mayElevate: () => boolean;
1007
+ /** `expiresAt` as a Date, null when not elevated. */
1008
+ readonly expiresAt: () => Date | null;
1009
+ /**
1010
+ * Every change of elevation state: a grant, a drop (by this person, by the
1011
+ * beacon, by the tripwire), an expiry, or an observed change of a live
1012
+ * grant's `expiresAt`. A refresh that reports the same state emits nothing,
1013
+ * so subscribers that refetch a listing on it never refetch for no reason.
1014
+ */
1015
+ readonly changes$: Subject<ElevationChange>;
1016
+ private expiryTimer;
1017
+ private pendingPrompt;
1018
+ private expiryAnnouncePending;
1019
+ private lastReconcileAt;
1020
+ constructor();
1021
+ /** Whether a prompt can be opened at all: this install offers elevation and a prompt is bound. */
1022
+ get canPrompt(): boolean;
1023
+ /** The manifest's API base, as {@link isThisApi} reads it. */
1024
+ get apiBase(): string;
1025
+ /** The manifest carries the URL; without it the service is inert, not broken. */
1026
+ get available(): boolean;
1027
+ private get url();
1028
+ /**
1029
+ * Whether a URL belongs to THIS API: a relative one under the manifest's
1030
+ * `apiBase`, or an absolute one on this origin under it. The interceptor
1031
+ * acts on a stamped refusal from here and from nowhere else -- a third
1032
+ * party answering 403 with a header of the same name is not an invitation
1033
+ * to elevate this session.
1034
+ */
1035
+ isThisApi(url: string): boolean;
1036
+ /** Ask the server. Applies the answer and announces it when it differs. */
1037
+ refresh(): Observable<ElevationState>;
1038
+ /**
1039
+ * Elevate with the person's own password and, when the switch is on, a
1040
+ * code from their own authenticator. Errors are the dialog's to branch on:
1041
+ * 403 not a member of the admin group, wrong password, or wrong / replayed
1042
+ * code, 428 a code is required, 409 this account cannot elevate yet, 429
1043
+ * throttled. They pass through
1044
+ * untouched, and never open the prompt (BYPASS_ELEVATION).
1045
+ */
1046
+ elevate(password: string, code?: string): Observable<ElevationState>;
1047
+ /** Drop the elevation now. Idempotent on the server: nothing live is still a 204. */
1048
+ drop(reason?: ElevationDropReason): Observable<ElevationState>;
1049
+ /**
1050
+ * What a person's explicit request for elevation calls -- an Elevate button,
1051
+ * or a control that already knows its action needs elevation: ask the
1052
+ * server whether this session is elevated, and only when it is not, open
1053
+ * the prompt. Nothing calls it on a response by itself (the interceptor
1054
+ * only tells {@link ELEVATION_NOTICE}). Elevated already is false, no
1055
+ * prompt: elevation would change nothing.
1056
+ */
1057
+ offerFor(refusal?: string): Observable<boolean>;
1058
+ /**
1059
+ * Open the prompt, or join the one already open. Resolves to whether the
1060
+ * session is elevated once the dialog closes.
1061
+ */
1062
+ requestElevation(request: ElevationPromptRequest): Observable<boolean>;
1063
+ private options;
1064
+ private inert;
1065
+ /**
1066
+ * Store the answer, schedule the expiry read, announce a change.
1067
+ *
1068
+ * `explicit` names the change when the caller knows it (a grant). For a
1069
+ * plain refresh the kind is derived from the two states: elevated -> not
1070
+ * is `expired` when the server says so and `dropped` otherwise (the person,
1071
+ * another tab, the beacon, the tripwire); not -> elevated is a grant seen
1072
+ * from another tab; a live grant whose `expiresAt` moved is `observed`.
1073
+ *
1074
+ * `prev` is read RAW rather than through `state()`: the demotion is not
1075
+ * wanted here (this answer supersedes the stored one either way), and a
1076
+ * read that demoted would queue an expiry announcement the server's own
1077
+ * answer is about to make.
1078
+ */
1079
+ private apply;
1080
+ private kindOf;
1081
+ /**
1082
+ * Tell the listings that this session's elevation ran out, once, and NOT
1083
+ * from inside the read that noticed: a subscriber refetches on this, and a
1084
+ * refetch started in the middle of a change detection pass is a write
1085
+ * during a read. The microtask runs at the end of the current task, before
1086
+ * anything can paint.
1087
+ *
1088
+ * A server answer arriving first clears the flag, so the announcement is
1089
+ * made by whichever of the two got there first, never by both. Other tabs
1090
+ * are NOT told: each one reaches the same conclusion from the same
1091
+ * `expiresAt` on its own read, so the storage write would only buy a
1092
+ * thundering herd of GETs.
1093
+ */
1094
+ private announceExpiryLater;
1095
+ /**
1096
+ * Ask the server again at `expiresAt` -- an OPTIMISATION, not the
1097
+ * mechanism. `state()` already reports an elapsed grant as expired without
1098
+ * being told, so a timer that a sleeping machine swallows, or that a
1099
+ * background tab throttles to once a minute, now costs freshness rather
1100
+ * than correctness. What it buys is the listing refetch happening at the
1101
+ * moment of expiry instead of at the next read.
1102
+ *
1103
+ * Do not flip a local flag here: the lifetime counts from the grant on the
1104
+ * server's clock, and if the server still says elevated at that moment
1105
+ * (skew), the honest thing is to ask again shortly.
1106
+ */
1107
+ private schedule;
1108
+ /**
1109
+ * One more attempt, then stop. Giving up is affordable now and was not
1110
+ * before: the state a failed read leaves behind is still read through the
1111
+ * arithmetic above, so an unreachable server cannot leave this tab
1112
+ * believing it is elevated. What is lost is only news from elsewhere -- a
1113
+ * grant or a drop in another session -- and that is what the tab picks up
1114
+ * when it next comes into view.
1115
+ */
1116
+ private scheduleRetry;
1117
+ private clearTimer;
1118
+ private announceToOtherTabs;
1119
+ private readonly onStorage;
1120
+ private readonly onFocus;
1121
+ private readonly onVisibilityChange;
1122
+ /**
1123
+ * The tab is back. Read first -- that alone ends an elevation that ran out
1124
+ * while it was away, with no request and no network to depend on -- then
1125
+ * ask the server for what the read cannot know: a grant or a drop made
1126
+ * elsewhere, or a lifetime the server moved.
1127
+ */
1128
+ private reconcile;
1129
+ static ɵfac: i0.ɵɵFactoryDeclaration<ElevationService, never>;
1130
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1131
+ }
1131
1132
  /**
1132
1133
  * A refusal for want of elevation is a 403 the server stamps `X-Elevation-Required`
1133
1134
  * where its gate refused a member who has not elevated ({@link isElevationRefusal}).
1134
- * This interceptor never opens the elevation prompt. The prompt opens only when a
1135
- * person asks for it: an Elevate button, or an action whose control already knows it
1136
- * needs elevation and offers it ({@link ElevationService.offerFor}).
1137
- *
1138
- * What it does with a stamped 403:
1139
- * - On a write a person made (POST, PUT, PATCH, DELETE without
1140
- * {@link BACKGROUND_REQUEST}): tells {@link ELEVATION_NOTICE}, which shows the
1141
- * refusal with an Elevate button. The request is not held and not repeated; the
1142
- * caller receives the 403 at once, and after elevating the person repeats the action.
1135
+ *
1136
+ * What it does with a stamped 403 (decided 2026-10-20 for a person's write):
1137
+ * - On a write a person made (POST, PUT, PATCH, DELETE without {@link BACKGROUND_REQUEST}):
1138
+ * the prompt opens THERE, above whatever the person is in -- an edit dialog stays open
1139
+ * beneath it with its input -- and the request is HELD. If the person elevates, it is
1140
+ * sent once more and the caller receives that answer, as if the first had been accepted;
1141
+ * if the session turns out to be elevated already (another tab), it is sent once more
1142
+ * without asking. If the person declines, {@link ELEVATION_NOTICE} is told -- "Elevation
1143
+ * required", naming the action, with an Elevate button -- and the caller receives the 403.
1144
+ * The repeat is sent past this interceptor, so a second refusal is a 403, never a second
1145
+ * prompt.
1143
1146
  * - On a read: nothing. Reads are what a page makes as it loads and what polls make in
1144
1147
  * the background, and the page shows its own state for a refused one -- an inline
1145
1148
  * "needs an elevated session" with an Elevate button, or the feature hidden.
1146
1149
  * - On a background request: nothing. Nobody is waiting on it.
1147
1150
  *
1148
- * Until 2026-10-08 any stamped 403 opened the prompt, so the setup reads of almost every
1149
- * console page, and a realtime token at every load, opened it for an administrator who
1150
- * had not elevated: 13 of 15 pages on a menu click, 15 of 15 on a reload (measured).
1151
+ * !! Why reads and background requests still open nothing. Until 2026-10-08 any stamped 403
1152
+ * opened the prompt, so the setup reads of almost every console page, and a realtime token at
1153
+ * every load, opened it for an administrator who had not elevated: 13 of 15 pages on a menu
1154
+ * click, 15 of 15 on a reload (measured). From then until 2026-10-20 a person's write opened
1155
+ * nothing either, only a toast -- which, with a dialog open, rendered under it, so the edit had
1156
+ * to be thrown away to elevate (measured on the served admin, Edit Node). A person's write is
1157
+ * the one refusal somebody is waiting on, which is why it is the one that asks.
1151
1158
  *
1152
1159
  * The elevation endpoint's own calls carry {@link BYPASS_ELEVATION}: a wrong password is a
1153
1160
  * 403 and is not a refused action. A stamped 403 from anywhere but this API is ignored:
1154
1161
  * the header is the server's word, and only this server's counts.
1155
1162
  */
1156
- declare const elevationInterceptor: HttpInterceptorFn;
1157
-
1158
- declare class LoginComponent {
1159
- identifier: string;
1160
- password: string;
1161
- readonly loading: i0.WritableSignal<boolean>;
1162
- readonly error: i0.WritableSignal<string | null>;
1163
- private readonly store;
1164
- private readonly router;
1165
- /** Shown when the guard signed an account out because the console is not granted to it. */
1166
- static readonly NO_CONSOLE_ACCESS = "This account has no access to the console.";
1167
- constructor();
1168
- submit(): void;
1169
- static ɵfac: i0.ɵɵFactoryDeclaration<LoginComponent, never>;
1170
- static ɵcmp: i0.ɵɵComponentDeclaration<LoginComponent, "coolms-admin-login", never, {}, {}, never, never, true, never>;
1171
- }
1172
-
1173
- declare class AppInitService {
1174
- private readonly http;
1175
- private readonly store;
1176
- private readonly router;
1177
- private readonly location;
1178
- private readonly prefs;
1179
- private readonly crossTabSync;
1180
- private readonly refreshCoordinator;
1181
- /** The only hardcoded URL -- everything else comes from the manifest. */
1182
- private readonly configUrl;
1183
- /**
1184
- * Emits (once) when load() has fully completed -- tokens settled, manifest
1185
- * set (or swallowed on error). authGuard subscribes to this so it never
1186
- * evaluates isAuthenticated against a partially-restored stale token.
1187
- */
1188
- private readonly initComplete;
1189
- readonly ready$: rxjs.Observable<void>;
1190
- load(): Promise<void>;
1191
- /**
1192
- * Confirm the restored session is still alive, settling AuthState before the
1193
- * app unblocks. Runs OUTSIDE the auth interceptor (BYPASS_AUTH) because the
1194
- * interceptor queues behind ready$, which has not fired yet at this point.
1195
- *
1196
- * - access token accepted -> done, session alive;
1197
- * - access token rejected -> attempt ONE refresh (access tokens are short-lived,
1198
- * so an expired one with a valid refresh token must
1199
- * stay logged in). The coordinator dispatches Logout
1200
- * on a refresh 401 and SetTokens on success; a
1201
- * transient failure (5xx/network) is swallowed so a
1202
- * momentary blip never kills a valid session.
1203
- */
1204
- private validateSession;
1205
- /** GET an authed endpoint with an explicit Bearer header, bypassing the interceptor. */
1206
- private probe;
1207
- static ɵfac: i0.ɵɵFactoryDeclaration<AppInitService, never>;
1208
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1209
- }
1210
-
1163
+ export declare const elevationInterceptor: HttpInterceptorFn;
1164
+ /**
1165
+ * The action a refused write was, in words for the person: what its caller said it is
1166
+ * ({@link ELEVATION_ACTION}), or else the method's verb and the resource the URL names --
1167
+ * "Saving navi nodes" for `PATCH /api/v1/navi/nodes/{id}`. Identifiers and the API base
1168
+ * are left out; they say nothing to the person who pressed the button.
1169
+ */
1170
+ export declare function actionOf(req: HttpRequest<unknown>, apiBase?: string): string;
1171
+ export declare class LoginComponent {
1172
+ identifier: string;
1173
+ password: string;
1174
+ readonly loading: import("@angular/core").WritableSignal<boolean>;
1175
+ readonly error: import("@angular/core").WritableSignal<string | null>;
1176
+ private readonly store;
1177
+ private readonly router;
1178
+ /** Shown when the guard signed an account out because the console is not granted to it. */
1179
+ static readonly NO_CONSOLE_ACCESS = "This account has no access to the console.";
1180
+ constructor();
1181
+ submit(): void;
1182
+ static ɵfac: i0.ɵɵFactoryDeclaration<LoginComponent, never>;
1183
+ static ɵcmp: i0.ɵɵComponentDeclaration<LoginComponent, "coolms-admin-login", never, {}, {}, never, never, true, never>;
1184
+ }
1185
+ export declare class AppInitService {
1186
+ private readonly http;
1187
+ private readonly store;
1188
+ private readonly router;
1189
+ private readonly location;
1190
+ private readonly prefs;
1191
+ private readonly crossTabSync;
1192
+ private readonly refreshCoordinator;
1193
+ /** The only hardcoded URL -- everything else comes from the manifest. */
1194
+ private readonly configUrl;
1195
+ /**
1196
+ * Emits (once) when load() has fully completed -- tokens settled, manifest
1197
+ * set (or swallowed on error). authGuard subscribes to this so it never
1198
+ * evaluates isAuthenticated against a partially-restored stale token.
1199
+ */
1200
+ private readonly initComplete;
1201
+ readonly ready$: import("rxjs").Observable<void>;
1202
+ load(): Promise<void>;
1203
+ /**
1204
+ * Confirm the restored session is still alive, settling AuthState before the
1205
+ * app unblocks. Runs OUTSIDE the auth interceptor (BYPASS_AUTH) because the
1206
+ * interceptor queues behind ready$, which has not fired yet at this point.
1207
+ *
1208
+ * - access token accepted -> done, session alive;
1209
+ * - access token rejected -> attempt ONE refresh (access tokens are short-lived,
1210
+ * so an expired one with a valid refresh token must
1211
+ * stay logged in). The coordinator dispatches Logout
1212
+ * on a refresh 401 and SetTokens on success; a
1213
+ * transient failure (5xx/network) is swallowed so a
1214
+ * momentary blip never kills a valid session.
1215
+ */
1216
+ private validateSession;
1217
+ /** GET an authed endpoint with an explicit Bearer header, bypassing the interceptor. */
1218
+ private probe;
1219
+ static ɵfac: i0.ɵɵFactoryDeclaration<AppInitService, never>;
1220
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1221
+ }
1211
1222
  /** What the server said about this sign-in and the console. */
1212
- type ConsoleAccess = 'granted' | 'refused' | 'unknown';
1223
+ export type ConsoleAccess = 'granted' | 'refused' | 'unknown';
1213
1224
  /**
1214
1225
  * Whether the signed-in account may use the console, and the console's endpoint
1215
1226
  * map when it may.
@@ -1239,32 +1250,31 @@ type ConsoleAccess = 'granted' | 'refused' | 'unknown';
1239
1250
  * interceptor dispatch Logout, which started another generation, which re-asked:
1240
1251
  * a loop for as long as the tab lived (found in review).
1241
1252
  */
1242
- declare class ConsoleAccessService {
1243
- /** The admin manifest's URL: fixed, like the theme config's, because nothing is known before it. */
1244
- static readonly URL = "/api/v1/admin/manifest";
1245
- /** The address before 2026-10-06, asked only when a server older than that answers 404 at {@link URL}. */
1246
- static readonly LEGACY_URL = "/api/v1/console/manifest";
1247
- /** The login page's query parameter value for "signed out: no console access". */
1248
- static readonly REFUSED_REASON = "no-console-access";
1249
- private readonly http;
1250
- private readonly store;
1251
- private answer;
1252
- private pending;
1253
- private generation;
1254
- constructor();
1255
- /** Ask once per sign-in; the manifest is merged into the app config when granted. */
1256
- ensure(): Promise<ConsoleAccess>;
1257
- /**
1258
- * Sign a refused account out: this sign-in ends on the server (its refresh token
1259
- * with it), as the account menu's sign-out does, and then in this browser --
1260
- * whatever the server answered, so a refused request cannot leave it signed in.
1261
- */
1262
- signOut(): Observable<void>;
1263
- private merge;
1264
- static ɵfac: i0.ɵɵFactoryDeclaration<ConsoleAccessService, never>;
1265
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1266
- }
1267
-
1253
+ export declare class ConsoleAccessService {
1254
+ /** The admin manifest's URL: fixed, like the theme config's, because nothing is known before it. */
1255
+ static readonly URL = "/api/v1/admin/manifest";
1256
+ /** The address before 2026-10-06, asked only when a server older than that answers 404 at {@link URL}. */
1257
+ static readonly LEGACY_URL = "/api/v1/console/manifest";
1258
+ /** The login page's query parameter value for "signed out: no console access". */
1259
+ static readonly REFUSED_REASON = "no-console-access";
1260
+ private readonly http;
1261
+ private readonly store;
1262
+ private answer;
1263
+ private pending;
1264
+ private generation;
1265
+ constructor();
1266
+ /** Ask once per sign-in; the manifest is merged into the app config when granted. */
1267
+ ensure(): Promise<ConsoleAccess>;
1268
+ /**
1269
+ * Sign a refused account out: this sign-in ends on the server (its refresh token
1270
+ * with it), as the account menu's sign-out does, and then in this browser --
1271
+ * whatever the server answered, so a refused request cannot leave it signed in.
1272
+ */
1273
+ signOut(): Observable<void>;
1274
+ private merge;
1275
+ static ɵfac: i0.ɵɵFactoryDeclaration<ConsoleAccessService, never>;
1276
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1277
+ }
1268
1278
  /**
1269
1279
  * Layout config DTO -- the shape served from
1270
1280
  * `GET /api/v1/config/layout/{id}`. Mirrors the YAML at
@@ -1290,93 +1300,93 @@ declare class ConsoleAccessService {
1290
1300
  * keys (e.g. `media:library`'s `naviGraph:` slot shorthand) keep working
1291
1301
  * without further interface changes.
1292
1302
  */
1293
- interface LayoutHeaderAction {
1294
- id: string;
1295
- label?: string;
1296
- icon?: string;
1297
- title?: string;
1298
- primary?: boolean;
1299
- danger?: boolean;
1300
- disabled?: boolean;
1301
- /**
1302
- * Optional capability gate evaluated FE-side against runtime state
1303
- * (e.g. `administer` / `delete`) -- the config declares WHICH actions
1304
- * exist + their gate; the FE decides if THIS user/row qualifies. Absent
1305
- * = always shown. Used by detail pages whose actions are config-driven
1306
- * (see `web:section-detail`).
1307
- *
1308
- * For a gate that depends on the RECORD rather than the user, prefer
1309
- * `showWhen` below: `requires` needs a page to interpret the token, so
1310
- * every new value costs a branch in TypeScript.
1311
- */
1312
- requires?: string;
1313
- /**
1314
- * Conditions on the page's published context, same grammar and same
1315
- * evaluator as a NaviGraph toolbar node, applied
1316
- * by {@link LayoutActionsService}:
1317
- *
1318
- * showWhen -- the action does not APPLY, so it is absent.
1319
- * disabledWhen -- temporarily UNAVAILABLE: stays put and greys out.
1320
- * busyWhen -- the action is RUNNING: greys out too, and may relabel.
1321
- * busyLabel -- label shown while `busyWhen` holds ("Saving...").
1322
- * activeWhen -- pressed/active styling.
1323
- *
1324
- * Two carriers (a layout config and a NaviGraph tree) but ONE vocabulary:
1325
- * a page that moves between them should not have to relearn how to say
1326
- * "not now".
1327
- */
1328
- showWhen?: Record<string, unknown>;
1329
- disabledWhen?: Record<string, unknown>;
1330
- busyWhen?: Record<string, unknown>;
1331
- busyLabel?: string;
1332
- activeWhen?: Record<string, unknown>;
1333
- }
1334
- interface LayoutSection {
1335
- title?: string;
1336
- /**
1337
- * When true, the layout shell pins the section to the top of the
1338
- * scroll container (CSS `position: sticky`). Used by inspector /
1339
- * filter pages where an input form should stay visible while the
1340
- * operator scrolls through results below. Currently honoured by
1341
- * cms-inspector-layout; cms-detail-layout will follow when it
1342
- * lands. Default false.
1343
- */
1344
- sticky?: boolean;
1345
- slots?: Record<string, {
1346
- component?: string;
1347
- [key: string]: unknown;
1348
- }>;
1349
- [key: string]: unknown;
1350
- }
1351
- interface LayoutConfig {
1352
- id?: string;
1353
- template?: string;
1354
- title?: string;
1355
- subtitle?: string;
1356
- icon?: string;
1357
- slots?: Record<string, unknown>;
1358
- sections?: Record<string, LayoutSection>;
1359
- headerActions?: LayoutHeaderAction[];
1360
- /**
1361
- * Actions rendered in the page's fixed bottom bar (`<cms-detail-footer>`)
1362
- * -- primary / destructive actions kept out of the cramped header.
1363
- * Same shape as `headerActions`; gated per-item via `requires`.
1364
- */
1365
- footerActions?: LayoutHeaderAction[];
1366
- permissions?: string[];
1367
- [key: string]: unknown;
1368
- }
1369
- interface DialogConfig {
1370
- [key: string]: unknown;
1371
- }
1372
- interface NavigraphConfig {
1373
- [key: string]: unknown;
1374
- }
1375
- interface FormConfig {
1376
- [key: string]: unknown;
1377
- }
1378
- interface DatagridConfig {
1303
+ export interface LayoutHeaderAction {
1304
+ id: string;
1305
+ label?: string;
1306
+ icon?: string;
1307
+ title?: string;
1308
+ primary?: boolean;
1309
+ danger?: boolean;
1310
+ disabled?: boolean;
1311
+ /**
1312
+ * Optional capability gate evaluated FE-side against runtime state
1313
+ * (e.g. `administer` / `delete`) -- the config declares WHICH actions
1314
+ * exist + their gate; the FE decides if THIS user/row qualifies. Absent
1315
+ * = always shown. Used by detail pages whose actions are config-driven
1316
+ * (see `web:section-detail`).
1317
+ *
1318
+ * For a gate that depends on the RECORD rather than the user, prefer
1319
+ * `showWhen` below: `requires` needs a page to interpret the token, so
1320
+ * every new value costs a branch in TypeScript.
1321
+ */
1322
+ requires?: string;
1323
+ /**
1324
+ * Conditions on the page's published context, same grammar and same
1325
+ * evaluator as a NaviGraph toolbar node, applied
1326
+ * by {@link LayoutActionsService}:
1327
+ *
1328
+ * showWhen -- the action does not APPLY, so it is absent.
1329
+ * disabledWhen -- temporarily UNAVAILABLE: stays put and greys out.
1330
+ * busyWhen -- the action is RUNNING: greys out too, and may relabel.
1331
+ * busyLabel -- label shown while `busyWhen` holds ("Saving...").
1332
+ * activeWhen -- pressed/active styling.
1333
+ *
1334
+ * Two carriers (a layout config and a NaviGraph tree) but ONE vocabulary:
1335
+ * a page that moves between them should not have to relearn how to say
1336
+ * "not now".
1337
+ */
1338
+ showWhen?: Record<string, unknown>;
1339
+ disabledWhen?: Record<string, unknown>;
1340
+ busyWhen?: Record<string, unknown>;
1341
+ busyLabel?: string;
1342
+ activeWhen?: Record<string, unknown>;
1343
+ }
1344
+ export interface LayoutSection {
1345
+ title?: string;
1346
+ /**
1347
+ * When true, the layout shell pins the section to the top of the
1348
+ * scroll container (CSS `position: sticky`). Used by inspector /
1349
+ * filter pages where an input form should stay visible while the
1350
+ * operator scrolls through results below. Currently honoured by
1351
+ * cms-inspector-layout; cms-detail-layout will follow when it
1352
+ * lands. Default false.
1353
+ */
1354
+ sticky?: boolean;
1355
+ slots?: Record<string, {
1356
+ component?: string;
1379
1357
  [key: string]: unknown;
1358
+ }>;
1359
+ [key: string]: unknown;
1360
+ }
1361
+ export interface LayoutConfig {
1362
+ id?: string;
1363
+ template?: string;
1364
+ title?: string;
1365
+ subtitle?: string;
1366
+ icon?: string;
1367
+ slots?: Record<string, unknown>;
1368
+ sections?: Record<string, LayoutSection>;
1369
+ headerActions?: LayoutHeaderAction[];
1370
+ /**
1371
+ * Actions rendered in the page's fixed bottom bar (`<cms-detail-footer>`)
1372
+ * -- primary / destructive actions kept out of the cramped header.
1373
+ * Same shape as `headerActions`; gated per-item via `requires`.
1374
+ */
1375
+ footerActions?: LayoutHeaderAction[];
1376
+ permissions?: string[];
1377
+ [key: string]: unknown;
1378
+ }
1379
+ export interface DialogConfig {
1380
+ [key: string]: unknown;
1381
+ }
1382
+ export interface NavigraphConfig {
1383
+ [key: string]: unknown;
1384
+ }
1385
+ export interface FormConfig {
1386
+ [key: string]: unknown;
1387
+ }
1388
+ export interface DatagridConfig {
1389
+ [key: string]: unknown;
1380
1390
  }
1381
1391
  /**
1382
1392
  * Fetches config YAMLs (served as JSON) from GET /api/v1/config/{type}/{id}.
@@ -1388,45 +1398,43 @@ interface DatagridConfig {
1388
1398
  * configBase is read from the API manifest (manifest.configBase) and falls
1389
1399
  * back to '/api/v1/config' when the manifest is not yet loaded.
1390
1400
  */
1391
- declare class ConfigService {
1392
- private readonly http;
1393
- private readonly store;
1394
- private readonly cache;
1395
- layout(id: string): Observable<LayoutConfig>;
1396
- dialog(id: string): Observable<DialogConfig>;
1397
- navigraph(slug: string): Observable<NavigraphConfig>;
1398
- form(id: string): Observable<FormConfig>;
1399
- datagrid(id: string): Observable<DatagridConfig>;
1400
- private fetch;
1401
- static ɵfac: i0.ɵɵFactoryDeclaration<ConfigService, never>;
1402
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1403
- }
1404
-
1405
- declare class SetAppConfig {
1406
- readonly config: ThemeConfigResponse;
1407
- static readonly type = "[AppConfig] Set";
1408
- constructor(config: ThemeConfigResponse);
1409
- }
1410
- interface AppConfigStateModel {
1411
- config: ThemeConfigResponse | null;
1412
- loaded: boolean;
1413
- }
1414
- declare class AppConfigState {
1415
- set(ctx: StateContext<AppConfigStateModel>, { config }: SetAppConfig): void;
1416
- /** The whole config as last set: the public theme config, with the console's manifest once granted. */
1417
- static config(state: AppConfigStateModel): ThemeConfigResponse | null;
1418
- static manifest(state: AppConfigStateModel): ApiManifest | null;
1419
- /**
1420
- * The viewer manifest. Null when the backend didn't ship a viewers
1421
- * section (no tagged providers); consumers should fall back to a
1422
- * "no preview" surface.
1423
- */
1424
- static viewers(state: AppConfigStateModel): ViewerApiManifest | null;
1425
- static loaded(state: AppConfigStateModel): boolean;
1426
- static ɵfac: i0.ɵɵFactoryDeclaration<AppConfigState, never>;
1427
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1428
- }
1429
-
1401
+ export declare class ConfigService {
1402
+ private readonly http;
1403
+ private readonly store;
1404
+ private readonly cache;
1405
+ layout(id: string): Observable<LayoutConfig>;
1406
+ dialog(id: string): Observable<DialogConfig>;
1407
+ navigraph(slug: string): Observable<NavigraphConfig>;
1408
+ form(id: string): Observable<FormConfig>;
1409
+ datagrid(id: string): Observable<DatagridConfig>;
1410
+ private fetch;
1411
+ static ɵfac: i0.ɵɵFactoryDeclaration<ConfigService, never>;
1412
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1413
+ }
1414
+ export declare class SetAppConfig {
1415
+ readonly config: ThemeConfigResponse;
1416
+ static readonly type = "[AppConfig] Set";
1417
+ constructor(config: ThemeConfigResponse);
1418
+ }
1419
+ export interface AppConfigStateModel {
1420
+ config: ThemeConfigResponse | null;
1421
+ loaded: boolean;
1422
+ }
1423
+ export declare class AppConfigState {
1424
+ set(ctx: StateContext<AppConfigStateModel>, { config }: SetAppConfig): void;
1425
+ /** The whole config as last set: the public theme config, with the console's manifest once granted. */
1426
+ static config(state: AppConfigStateModel): ThemeConfigResponse | null;
1427
+ static manifest(state: AppConfigStateModel): ApiManifest | null;
1428
+ /**
1429
+ * The viewer manifest. Null when the backend didn't ship a viewers
1430
+ * section (no tagged providers); consumers should fall back to a
1431
+ * "no preview" surface.
1432
+ */
1433
+ static viewers(state: AppConfigStateModel): ViewerApiManifest | null;
1434
+ static loaded(state: AppConfigStateModel): boolean;
1435
+ static ɵfac: i0.ɵɵFactoryDeclaration<AppConfigState, never>;
1436
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1437
+ }
1430
1438
  /**
1431
1439
  * Converts any caught error into a human-readable message.
1432
1440
  *
@@ -1437,17 +1445,16 @@ declare class AppConfigState {
1437
1445
  * 4. error.violations[] (constraint violations)
1438
1446
  * 5. Status-based fallback
1439
1447
  */
1440
- declare class ErrorHandlerService {
1441
- humanize(err: unknown): string;
1442
- private statusMessage;
1443
- static ɵfac: i0.ɵɵFactoryDeclaration<ErrorHandlerService, never>;
1444
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1448
+ export declare class ErrorHandlerService {
1449
+ humanize(err: unknown): string;
1450
+ private statusMessage;
1451
+ static ɵfac: i0.ɵɵFactoryDeclaration<ErrorHandlerService, never>;
1452
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1445
1453
  }
1446
-
1447
1454
  /** What the user PICKED. `system` defers to the OS. */
1448
- type ThemeChoice = 'light' | 'dark' | 'system';
1455
+ export type ThemeChoice = 'light' | 'dark' | 'system';
1449
1456
  /** What that resolves to right now -- the only two the stylesheet knows. */
1450
- type ResolvedTheme = 'light' | 'dark';
1457
+ export type ResolvedTheme = 'light' | 'dark';
1451
1458
  /**
1452
1459
  * Applies the user's theme by setting `data-theme` on the document element.
1453
1460
  *
@@ -1461,78 +1468,77 @@ type ResolvedTheme = 'light' | 'dark';
1461
1468
  * cached for the SPA lifetime, with `update()` called by the Profile page after
1462
1469
  * a save so the change lands without a refetch.
1463
1470
  */
1464
- declare class ThemeService {
1465
- private readonly api;
1466
- private readonly store;
1467
- private readonly _choice;
1468
- private readonly _systemDark;
1469
- private readonly _userAccent;
1470
- private readonly _platformAccent;
1471
- private loadOnce$?;
1472
- /** The user's stored choice, including `system`. */
1473
- readonly choice: i0.Signal<ThemeChoice>;
1474
- /** This user's personal override, independent of the deployment's. */
1475
- readonly userAccent: i0.Signal<string | null>;
1476
- /** The deployment's brand accent from the API manifest, if it set one. */
1477
- readonly platformAccent: i0.Signal<string | null>;
1478
- /**
1479
- * The colour actually painted: personal override, else the deployment's,
1480
- * else null -- which leaves the stylesheet's own accent in place.
1481
- *
1482
- * Three rungs and each is a real state. A user who has chosen nothing is
1483
- * NOT the same as one who chose the deployment's colour: clear the
1484
- * deployment's brand and the first user follows it, the second does not.
1485
- */
1486
- readonly accent: i0.Signal<string | null>;
1487
- /** The choice with `system` resolved against the OS setting. */
1488
- readonly resolved: i0.Signal<ResolvedTheme>;
1489
- constructor();
1490
- /**
1491
- * One-shot load of the stored preference. Safe to call more than once and
1492
- * safe for anonymous users -- a failure leaves the cached/system value in
1493
- * place rather than forcing light, so the login screen still matches the OS.
1494
- */
1495
- ensureLoaded(): Observable<ThemeChoice>;
1496
- /**
1497
- * Called by the Profile page after the Preferences tab saves, so the admin
1498
- * re-themes on the spot. Ignores anything that is not a known choice --
1499
- * the section PATCH returns the whole merged bag, not just this field.
1500
- */
1501
- update(value: unknown): void;
1502
- /**
1503
- * Companion to {@link update} for the accent field of the same save.
1504
- * An explicit null clears the override; anything malformed is ignored
1505
- * rather than applied, so a bad value can never blank the admin's accent.
1506
- */
1507
- updateAccent(value: unknown): void;
1508
- private set;
1509
- /**
1510
- * The manifest is loaded at bootstrap, well before the authenticated shell
1511
- * calls this, so a snapshot read is enough and no subscription is needed.
1512
- * A malformed value is ignored rather than applied -- the container refuses
1513
- * to build with one, but the manifest crosses the network all the same.
1514
- */
1515
- private readPlatformAccent;
1516
- private setAccent;
1517
- /**
1518
- * Writes the override onto the document element, where an inline style
1519
- * outranks every stylesheet rule -- including the dark block -- so one
1520
- * declaration re-points the accent in both themes.
1521
- *
1522
- * Only --cms-accent is set, not the -hover/-light/-text members of the
1523
- * family. Deriving those from an arbitrary user colour needs a colour model
1524
- * the palette does not have yet, and guessing them would produce hover
1525
- * states that clash with the very colour the user chose. Until then the
1526
- * override moves the brand fill and leaves its supporting tones alone.
1527
- */
1528
- private applyAccent;
1529
- private applyToDocument;
1530
- private static readCache;
1531
- private static readAccentCache;
1532
- static ɵfac: i0.ɵɵFactoryDeclaration<ThemeService, never>;
1533
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1534
- }
1535
-
1471
+ export declare class ThemeService {
1472
+ private readonly api;
1473
+ private readonly store;
1474
+ private readonly _choice;
1475
+ private readonly _systemDark;
1476
+ private readonly _userAccent;
1477
+ private readonly _platformAccent;
1478
+ private loadOnce$?;
1479
+ /** The user's stored choice, including `system`. */
1480
+ readonly choice: import("@angular/core").Signal<ThemeChoice>;
1481
+ /** This user's personal override, independent of the deployment's. */
1482
+ readonly userAccent: import("@angular/core").Signal<string | null>;
1483
+ /** The deployment's brand accent from the API manifest, if it set one. */
1484
+ readonly platformAccent: import("@angular/core").Signal<string | null>;
1485
+ /**
1486
+ * The colour actually painted: personal override, else the deployment's,
1487
+ * else null -- which leaves the stylesheet's own accent in place.
1488
+ *
1489
+ * Three rungs and each is a real state. A user who has chosen nothing is
1490
+ * NOT the same as one who chose the deployment's colour: clear the
1491
+ * deployment's brand and the first user follows it, the second does not.
1492
+ */
1493
+ readonly accent: import("@angular/core").Signal<string | null>;
1494
+ /** The choice with `system` resolved against the OS setting. */
1495
+ readonly resolved: import("@angular/core").Signal<ResolvedTheme>;
1496
+ constructor();
1497
+ /**
1498
+ * One-shot load of the stored preference. Safe to call more than once and
1499
+ * safe for anonymous users -- a failure leaves the cached/system value in
1500
+ * place rather than forcing light, so the login screen still matches the OS.
1501
+ */
1502
+ ensureLoaded(): Observable<ThemeChoice>;
1503
+ /**
1504
+ * Called by the Profile page after the Preferences tab saves, so the admin
1505
+ * re-themes on the spot. Ignores anything that is not a known choice --
1506
+ * the section PATCH returns the whole merged bag, not just this field.
1507
+ */
1508
+ update(value: unknown): void;
1509
+ /**
1510
+ * Companion to {@link update} for the accent field of the same save.
1511
+ * An explicit null clears the override; anything malformed is ignored
1512
+ * rather than applied, so a bad value can never blank the admin's accent.
1513
+ */
1514
+ updateAccent(value: unknown): void;
1515
+ private set;
1516
+ /**
1517
+ * The manifest is loaded at bootstrap, well before the authenticated shell
1518
+ * calls this, so a snapshot read is enough and no subscription is needed.
1519
+ * A malformed value is ignored rather than applied -- the container refuses
1520
+ * to build with one, but the manifest crosses the network all the same.
1521
+ */
1522
+ private readPlatformAccent;
1523
+ private setAccent;
1524
+ /**
1525
+ * Writes the override onto the document element, where an inline style
1526
+ * outranks every stylesheet rule -- including the dark block -- so one
1527
+ * declaration re-points the accent in both themes.
1528
+ *
1529
+ * Only --cms-accent is set, not the -hover/-light/-text members of the
1530
+ * family. Deriving those from an arbitrary user colour needs a colour model
1531
+ * the palette does not have yet, and guessing them would produce hover
1532
+ * states that clash with the very colour the user chose. Until then the
1533
+ * override moves the brand fill and leaves its supporting tones alone.
1534
+ */
1535
+ private applyAccent;
1536
+ private applyToDocument;
1537
+ private static readCache;
1538
+ private static readAccentCache;
1539
+ static ɵfac: i0.ɵɵFactoryDeclaration<ThemeService, never>;
1540
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1541
+ }
1536
1542
  /**
1537
1543
  * The platform's loading indicator: the exclamation mark from Cool!MS,
1538
1544
  * drawing itself.
@@ -1559,26 +1565,41 @@ declare class ThemeService {
1559
1565
  * Under `prefers-reduced-motion` it holds still and only breathes, because
1560
1566
  * motion sickness is not a stylistic preference.
1561
1567
  */
1562
- declare class CmsLoaderComponent {
1563
- /** Optional caption. Absent renders the mark alone, which suits a small inline wait. */
1564
- readonly label: i0.InputSignal<string>;
1565
- /** Cover the positioned ancestor rather than sitting in the flow. */
1566
- readonly overlay: i0.InputSignal<boolean>;
1567
- /**
1568
- * Row layout at text scale, for a wait inside a row, a status bar or a
1569
- * button. Override `--cms-loader-size` to tune the mark.
1570
- */
1571
- readonly inline: i0.InputSignal<boolean>;
1572
- /**
1573
- * A clip-path needs a document-unique id, and this component is used more
1574
- * than once per page -- two loaders sharing an id makes the second one clip
1575
- * against the first one's rectangle and stop animating.
1576
- */
1577
- protected readonly clipId: string;
1578
- static ɵfac: i0.ɵɵFactoryDeclaration<CmsLoaderComponent, never>;
1579
- static ɵcmp: i0.ɵɵComponentDeclaration<CmsLoaderComponent, "cms-loader", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "overlay": { "alias": "overlay"; "required": false; "isSignal": true; }; "inline": { "alias": "inline"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1580
- }
1581
-
1568
+ export declare class CmsLoaderComponent {
1569
+ /** Optional caption. Absent renders the mark alone, which suits a small inline wait. */
1570
+ readonly label: import("@angular/core").InputSignal<string>;
1571
+ /** Cover the positioned ancestor rather than sitting in the flow. */
1572
+ readonly overlay: import("@angular/core").InputSignal<boolean>;
1573
+ /**
1574
+ * Row layout at text scale, for a wait inside a row, a status bar or a
1575
+ * button. Override `--cms-loader-size` to tune the mark.
1576
+ */
1577
+ readonly inline: import("@angular/core").InputSignal<boolean>;
1578
+ /**
1579
+ * A clip-path needs a document-unique id, and this component is used more
1580
+ * than once per page -- two loaders sharing an id makes the second one clip
1581
+ * against the first one's rectangle and stop animating.
1582
+ */
1583
+ protected readonly clipId: string;
1584
+ static ɵfac: i0.ɵɵFactoryDeclaration<CmsLoaderComponent, never>;
1585
+ static ɵcmp: i0.ɵɵComponentDeclaration<CmsLoaderComponent, "cms-loader", never, {
1586
+ "label": {
1587
+ "alias": "label";
1588
+ "required": false;
1589
+ "isSignal": true;
1590
+ };
1591
+ "overlay": {
1592
+ "alias": "overlay";
1593
+ "required": false;
1594
+ "isSignal": true;
1595
+ };
1596
+ "inline": {
1597
+ "alias": "inline";
1598
+ "required": false;
1599
+ "isSignal": true;
1600
+ };
1601
+ }, {}, never, never, true, never>;
1602
+ }
1582
1603
  /**
1583
1604
  * Unified user preferences service -- all preference types share one
1584
1605
  * localStorage key (`coolms_ui_prefs`) and one debounced server-sync pipeline.
@@ -1604,106 +1625,105 @@ declare class CmsLoaderComponent {
1604
1625
  * terminal.height
1605
1626
  * terminal.open
1606
1627
  */
1607
- declare class UserPreferencesService {
1608
- private readonly http;
1609
- private readonly store;
1610
- private readonly destroyRef;
1611
- private readonly STORAGE_KEY;
1612
- private readonly SYNC_DELAY;
1613
- private syncSubject;
1614
- constructor();
1615
- /**
1616
- * Write all prefs to localStorage and schedule a debounced server sync.
1617
- */
1618
- private save;
1619
- /**
1620
- * Read the entire prefs object from localStorage, applying legacy key
1621
- * migration on the first read after an upgrade.
1622
- */
1623
- private load;
1624
- /**
1625
- * One-time migration of legacy flat keys to the new nested naming convention.
1626
- * Mutates `all` in place and re-persists only when something changed.
1627
- */
1628
- private migrate;
1629
- /**
1630
- * Seed localStorage from server preferences.
1631
- * Called after login and on every session restore (page reload).
1632
- *
1633
- * Strategy:
1634
- * - No local prefs yet (new device / clean install) -> use server prefs as-is.
1635
- * - Local prefs already exist -> merge with local taking priority at each
1636
- * top-level namespace. This ensures that locally-persisted state
1637
- * (panel widths, last path, etc.) is never overwritten by the stale copy
1638
- * that was saved in the auth token at login time.
1639
- */
1640
- loadFromServer(uiPrefs: Record<string, unknown>): void;
1641
- private syncToServer;
1642
- /**
1643
- * Get stored preferences for a specific grid.
1644
- */
1645
- getGridPref(gridId: string): DataGridPreference | null;
1646
- /**
1647
- * Set visible column order for a grid.
1648
- * Applies to localStorage instantly; server sync is debounced.
1649
- */
1650
- setColumns(gridId: string, columns: string[]): void;
1651
- /**
1652
- * Record a column width for a grid column.
1653
- * Applies to localStorage instantly; server sync is debounced.
1654
- */
1655
- setColumnWidth(gridId: string, field: string, width: number): void;
1656
- /**
1657
- * Remove all preferences for a specific grid and immediately sync to server.
1658
- */
1659
- resetGrid(gridId: string): void;
1660
- private updateGrid;
1661
- /**
1662
- * Get persisted terminal panel height.
1663
- */
1664
- getTerminalHeight(): number | null;
1665
- /**
1666
- * Persist terminal panel height.
1667
- */
1668
- setTerminalHeight(height: number): void;
1669
- /**
1670
- * Get persisted state for a resizable panel identified by key.
1671
- * Used by ExplorerLayoutComponent to persist panel widths across sessions.
1672
- */
1673
- getPanelState(key: string): {
1674
- width: number;
1675
- collapsed: boolean;
1676
- } | null;
1677
- /**
1678
- * Persist generic panel state by key.
1679
- * Applies to localStorage instantly; server sync is debounced.
1680
- */
1681
- setPanelState(key: string, width: number, collapsed: boolean): void;
1682
- /**
1683
- * Get all persisted state for a page namespace (e.g. 'vfs', 'media', 'nav').
1684
- */
1685
- getPageState<T>(pageKey: string): T | null;
1686
- /**
1687
- * Merge partial state into a page namespace.
1688
- * Existing keys in the namespace are preserved; only provided keys are updated.
1689
- * Applies to localStorage instantly; server sync is debounced.
1690
- */
1691
- setPageState(pageKey: string, state: Record<string, unknown>): void;
1692
- /** @deprecated Use getPageState('media') instead */
1693
- getMediaViewMode(): string | null;
1694
- /** @deprecated Use setPageState('media', ...) instead */
1695
- setMediaViewMode(mode: string): void;
1696
- /** @deprecated Use getPanelState('media_panel_left') instead */
1697
- getMediaCollectionsPanel(): {
1698
- width: number;
1699
- collapsed: boolean;
1700
- } | null;
1701
- /** @deprecated Use setPanelState('media_panel_left', ...) instead */
1702
- setMediaCollectionsPanel(width: number, collapsed: boolean): void;
1703
- static ɵfac: i0.ɵɵFactoryDeclaration<UserPreferencesService, never>;
1704
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1705
- }
1706
-
1628
+ export declare class UserPreferencesService {
1629
+ private readonly http;
1630
+ private readonly store;
1631
+ private readonly destroyRef;
1632
+ private readonly STORAGE_KEY;
1633
+ private readonly SYNC_DELAY;
1634
+ private syncSubject;
1635
+ constructor();
1636
+ /**
1637
+ * Write all prefs to localStorage and schedule a debounced server sync.
1638
+ */
1639
+ private save;
1640
+ /**
1641
+ * Read the entire prefs object from localStorage, applying legacy key
1642
+ * migration on the first read after an upgrade.
1643
+ */
1644
+ private load;
1645
+ /**
1646
+ * One-time migration of legacy flat keys to the new nested naming convention.
1647
+ * Mutates `all` in place and re-persists only when something changed.
1648
+ */
1649
+ private migrate;
1650
+ /**
1651
+ * Seed localStorage from server preferences.
1652
+ * Called after login and on every session restore (page reload).
1653
+ *
1654
+ * Strategy:
1655
+ * - No local prefs yet (new device / clean install) -> use server prefs as-is.
1656
+ * - Local prefs already exist -> merge with local taking priority at each
1657
+ * top-level namespace. This ensures that locally-persisted state
1658
+ * (panel widths, last path, etc.) is never overwritten by the stale copy
1659
+ * that was saved in the auth token at login time.
1660
+ */
1661
+ loadFromServer(uiPrefs: Record<string, unknown>): void;
1662
+ private syncToServer;
1663
+ /**
1664
+ * Get stored preferences for a specific grid.
1665
+ */
1666
+ getGridPref(gridId: string): DataGridPreference | null;
1667
+ /**
1668
+ * Set visible column order for a grid.
1669
+ * Applies to localStorage instantly; server sync is debounced.
1670
+ */
1671
+ setColumns(gridId: string, columns: string[]): void;
1672
+ /**
1673
+ * Record a column width for a grid column.
1674
+ * Applies to localStorage instantly; server sync is debounced.
1675
+ */
1676
+ setColumnWidth(gridId: string, field: string, width: number): void;
1677
+ /**
1678
+ * Remove all preferences for a specific grid and immediately sync to server.
1679
+ */
1680
+ resetGrid(gridId: string): void;
1681
+ private updateGrid;
1682
+ /**
1683
+ * Get persisted terminal panel height.
1684
+ */
1685
+ getTerminalHeight(): number | null;
1686
+ /**
1687
+ * Persist terminal panel height.
1688
+ */
1689
+ setTerminalHeight(height: number): void;
1690
+ /**
1691
+ * Get persisted state for a resizable panel identified by key.
1692
+ * Used by ExplorerLayoutComponent to persist panel widths across sessions.
1693
+ */
1694
+ getPanelState(key: string): {
1695
+ width: number;
1696
+ collapsed: boolean;
1697
+ } | null;
1698
+ /**
1699
+ * Persist generic panel state by key.
1700
+ * Applies to localStorage instantly; server sync is debounced.
1701
+ */
1702
+ setPanelState(key: string, width: number, collapsed: boolean): void;
1703
+ /**
1704
+ * Get all persisted state for a page namespace (e.g. 'vfs', 'media', 'nav').
1705
+ */
1706
+ getPageState<T>(pageKey: string): T | null;
1707
+ /**
1708
+ * Merge partial state into a page namespace.
1709
+ * Existing keys in the namespace are preserved; only provided keys are updated.
1710
+ * Applies to localStorage instantly; server sync is debounced.
1711
+ */
1712
+ setPageState(pageKey: string, state: Record<string, unknown>): void;
1713
+ /** @deprecated Use getPageState('media') instead */
1714
+ getMediaViewMode(): string | null;
1715
+ /** @deprecated Use setPageState('media', ...) instead */
1716
+ setMediaViewMode(mode: string): void;
1717
+ /** @deprecated Use getPanelState('media_panel_left') instead */
1718
+ getMediaCollectionsPanel(): {
1719
+ width: number;
1720
+ collapsed: boolean;
1721
+ } | null;
1722
+ /** @deprecated Use setPanelState('media_panel_left', ...) instead */
1723
+ setMediaCollectionsPanel(width: number, collapsed: boolean): void;
1724
+ static ɵfac: i0.ɵɵFactoryDeclaration<UserPreferencesService, never>;
1725
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1726
+ }
1707
1727
  /**
1708
1728
  * Sidebar collapse state -- two independent dimensions:
1709
1729
  *
@@ -1723,35 +1743,34 @@ declare class UserPreferencesService {
1723
1743
  * to defaulting un-collapsed -- the toggles still work for the session,
1724
1744
  * just don't persist.
1725
1745
  */
1726
- declare class SidebarStateService {
1727
- private static readonly STORAGE_KEY;
1728
- private static readonly STORAGE_KEY_SECTIONS;
1729
- private static readonly STORAGE_KEY_ITEMS;
1730
- readonly collapsed: i0.WritableSignal<boolean>;
1731
- readonly collapsedSections: i0.WritableSignal<Set<string>>;
1732
- readonly collapsedItems: i0.WritableSignal<Set<string>>;
1733
- toggle(): void;
1734
- isSectionCollapsed(id: string): boolean;
1735
- toggleSection(id: string): void;
1736
- /**
1737
- * Force a section to be expanded. Used when navigation lands on
1738
- * a route inside a manually-collapsed section -- it would be
1739
- * confusing to leave the active item hidden. The user can
1740
- * collapse the section again afterwards.
1741
- */
1742
- ensureExpanded(id: string): void;
1743
- isItemCollapsed(id: string): boolean;
1744
- toggleItem(id: string): void;
1745
- private readPersisted;
1746
- private persist;
1747
- private readSectionsPersisted;
1748
- private persistSections;
1749
- private readItemsPersisted;
1750
- private persistItems;
1751
- static ɵfac: i0.ɵɵFactoryDeclaration<SidebarStateService, never>;
1752
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1753
- }
1754
-
1746
+ export declare class SidebarStateService {
1747
+ private static readonly STORAGE_KEY;
1748
+ private static readonly STORAGE_KEY_SECTIONS;
1749
+ private static readonly STORAGE_KEY_ITEMS;
1750
+ readonly collapsed: import("@angular/core").WritableSignal<boolean>;
1751
+ readonly collapsedSections: import("@angular/core").WritableSignal<Set<string>>;
1752
+ readonly collapsedItems: import("@angular/core").WritableSignal<Set<string>>;
1753
+ toggle(): void;
1754
+ isSectionCollapsed(id: string): boolean;
1755
+ toggleSection(id: string): void;
1756
+ /**
1757
+ * Force a section to be expanded. Used when navigation lands on
1758
+ * a route inside a manually-collapsed section -- it would be
1759
+ * confusing to leave the active item hidden. The user can
1760
+ * collapse the section again afterwards.
1761
+ */
1762
+ ensureExpanded(id: string): void;
1763
+ isItemCollapsed(id: string): boolean;
1764
+ toggleItem(id: string): void;
1765
+ private readPersisted;
1766
+ private persist;
1767
+ private readSectionsPersisted;
1768
+ private persistSections;
1769
+ private readItemsPersisted;
1770
+ private persistItems;
1771
+ static ɵfac: i0.ɵɵFactoryDeclaration<SidebarStateService, never>;
1772
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1773
+ }
1755
1774
  /**
1756
1775
  * One row returned by the `/api/entity-search` endpoint. `id` is
1757
1776
  * what the picker emits via its `valueChange` output (and what the
@@ -1761,10 +1780,10 @@ declare class SidebarStateService {
1761
1780
  * when there is nothing to show; receiving `null` from a custom
1762
1781
  * resolver is normalized to `undefined` here.
1763
1782
  */
1764
- interface EntitySearchResult {
1765
- readonly id: string | number;
1766
- readonly label: string;
1767
- readonly secondary?: string;
1783
+ export interface EntitySearchResult {
1784
+ readonly id: string | number;
1785
+ readonly label: string;
1786
+ readonly secondary?: string;
1768
1787
  }
1769
1788
  /**
1770
1789
  * HTTP client for the Phase 2a `/api/entity-search` endpoint.
@@ -1772,55 +1791,64 @@ interface EntitySearchResult {
1772
1791
  * Permission filtering happens at the backend resolver layer --
1773
1792
  * this service is a thin RPC.
1774
1793
  */
1775
- declare class EntitySearchService {
1776
- private readonly http;
1777
- /**
1778
- * @param entityType Fully-qualified PHP class name of the entity
1779
- * being searched (e.g. the backend's User entity).
1780
- * @param query Free-text search input. Empty string is
1781
- * valid -- the backend returns the default
1782
- * listing for that entity type.
1783
- * @param limit Max rows to return; clamped server-side to
1784
- * [1, 100].
1785
- */
1786
- search(entityType: string, query: string, limit?: number): Observable<EntitySearchResult[]>;
1787
- static ɵfac: i0.ɵɵFactoryDeclaration<EntitySearchService, never>;
1788
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1789
- }
1790
-
1794
+ export declare class EntitySearchService {
1795
+ private readonly http;
1796
+ /**
1797
+ * @param entityType Fully-qualified PHP class name of the entity
1798
+ * being searched (e.g. the backend's User entity).
1799
+ * @param query Free-text search input. Empty string is
1800
+ * valid -- the backend returns the default
1801
+ * listing for that entity type.
1802
+ * @param limit Max rows to return; clamped server-side to
1803
+ * [1, 100].
1804
+ */
1805
+ search(entityType: string, query: string, limit?: number): Observable<EntitySearchResult[]>;
1806
+ static ɵfac: i0.ɵɵFactoryDeclaration<EntitySearchService, never>;
1807
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1808
+ }
1791
1809
  /**
1792
1810
  * Well-known click target values embedded in meta.target.
1793
1811
  * 'action.logout' -- dispatch Logout action
1794
1812
  * 'route' -- navigate via routerLink (default when target absent)
1795
1813
  * '_blank' -- open in a new tab (hrefOverride must be set)
1796
1814
  */
1797
- type NaviGraphTarget = 'action.logout' | 'route' | '_blank' | (string & {});
1798
- interface NaviGraphMeta {
1799
- readonly component?: string;
1800
- readonly icon?: string;
1801
- readonly routerLink?: string;
1802
- readonly target?: NaviGraphTarget;
1803
- readonly requiredRole?: string;
1804
- readonly voterAttribute?: string;
1805
- readonly contributor?: string;
1806
- readonly [key: string]: unknown;
1815
+ export type NaviGraphTarget = 'action.logout' | 'route' | '_blank' | (string & {});
1816
+ export interface NaviGraphMeta {
1817
+ readonly component?: string;
1818
+ readonly icon?: string;
1819
+ readonly routerLink?: string;
1820
+ readonly target?: NaviGraphTarget;
1821
+ readonly requiredRole?: string;
1822
+ readonly voterAttribute?: string;
1823
+ readonly contributor?: string;
1824
+ readonly [key: string]: unknown;
1807
1825
  }
1826
+ /**
1827
+ * What the server says this account may do with a node, as it answered the graph:
1828
+ * 'allowed' -- its own rights open it; shown as usual;
1829
+ * 'elevate' -- a member of admin, not elevated, whose own rights refuse it and whom elevation would let in;
1830
+ * shown with a lock, and opening it asks for elevation first.
1831
+ * A node anyone else may not open is not in the answer at all. Absent (an older server) reads as 'allowed'.
1832
+ */
1833
+ export type NaviAccess = 'allowed' | 'elevate';
1808
1834
  /** Flat node as returned from GET /navi/trees/{slug}/graph */
1809
- interface NaviGraphNodeFlat {
1810
- readonly id: string;
1811
- readonly path: string;
1812
- readonly title: string;
1813
- readonly parentId: string | null;
1814
- readonly sortOrder: number;
1815
- readonly isActive: boolean;
1816
- readonly isVisible: boolean;
1817
- readonly meta: NaviGraphMeta;
1818
- }
1835
+ export interface NaviGraphNodeFlat {
1836
+ readonly id: string;
1837
+ readonly path: string;
1838
+ readonly title: string;
1839
+ readonly parentId: string | null;
1840
+ readonly sortOrder: number;
1841
+ readonly isActive: boolean;
1842
+ readonly isVisible: boolean;
1843
+ readonly meta: NaviGraphMeta;
1844
+ readonly access?: NaviAccess;
1845
+ }
1846
+ /** The node's access as the server answered it; 'allowed' when the server said nothing (an older server). */
1847
+ export declare function naviAccessOf(node: Pick<NaviGraphNodeFlat, 'access'>): NaviAccess;
1819
1848
  /** Same node enriched with resolved children after client-side tree build */
1820
- interface NaviGraphNode extends NaviGraphNodeFlat {
1821
- readonly children: NaviGraphNode[];
1849
+ export interface NaviGraphNode extends NaviGraphNodeFlat {
1850
+ readonly children: NaviGraphNode[];
1822
1851
  }
1823
-
1824
1852
  /**
1825
1853
  * Loads, caches, and drives admin NaviGraph trees.
1826
1854
  *
@@ -1829,59 +1857,65 @@ interface NaviGraphNode extends NaviGraphNodeFlat {
1829
1857
  * const items = naviGraphService.adminNav(); // signal<NaviGraphNode[]>
1830
1858
  * naviGraphService.handleClick(node);
1831
1859
  */
1832
- declare class NaviGraphService {
1833
- private readonly http;
1834
- private readonly router;
1835
- private readonly store;
1836
- private readonly errors;
1837
- /** Resolved root nodes for navi.admin (sidebar). */
1838
- readonly adminNav: i0.WritableSignal<NaviGraphNode[]>;
1839
- /** Resolved root nodes for navi.admin.topbar. */
1840
- readonly topbarNav: i0.WritableSignal<NaviGraphNode[]>;
1841
- private readonly cache;
1842
- loadAdminNav(url: string): Observable<NaviGraphNode[]>;
1843
- loadTopbarNav(url: string): Observable<NaviGraphNode[]>;
1844
- /**
1845
- * Handle a nav-node click.
1846
- *
1847
- * Routing logic based on meta.target:
1848
- * 'action.logout' -- dispatch Logout
1849
- * '_blank' -- window.open (uses node.path as href)
1850
- * default -- router.navigate with meta.routerLink or node.path
1851
- */
1852
- handleClick(node: NaviGraphNode): void;
1853
- /**
1854
- * Evaluate `showWhen` meta condition against a record.
1855
- * Nodes with no `showWhen` are always visible.
1856
- * Supports nested AND/OR logic: { and: [...] } / { or: [...] }
1857
- */
1858
- isVisible(node: NaviGraphNode, record: Record<string, unknown>): boolean;
1859
- /**
1860
- * Standalone `showWhen` evaluator for callers that don't have a
1861
- * full `NaviGraphNode` (e.g. a grid's row context menu -- a
1862
- * `rowAction.showWhen` predicate is evaluated against the row's
1863
- * own projection). `null`/`undefined` predicates default to
1864
- * visible, mirroring `isVisible`'s "no rule = always shown"
1865
- * semantics.
1866
- */
1867
- matchesShowWhen(showWhen: Record<string, unknown> | undefined | null, record: Record<string, unknown>): boolean;
1868
- private evalCondition;
1869
- /**
1870
- * Fetch a graph endpoint and return a nested tree.
1871
- * Handles both compact JSON-LD IRIs (`member`) and
1872
- * the legacy Hydra key (`hydra:member`).
1873
- * Results are cached by URL for the lifetime of the service instance.
1874
- */
1875
- loadTree(url: string): Observable<NaviGraphNode[]>;
1876
- /**
1877
- * Build a nested tree from a flat array of nodes.
1878
- * Nodes are sorted by sortOrder at each level.
1879
- */
1880
- private buildTree;
1881
- static ɵfac: i0.ɵɵFactoryDeclaration<NaviGraphService, never>;
1882
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1883
- }
1884
-
1860
+ export declare class NaviGraphService {
1861
+ private readonly http;
1862
+ private readonly router;
1863
+ private readonly store;
1864
+ private readonly errors;
1865
+ /** Resolved root nodes for navi.admin (sidebar). */
1866
+ readonly adminNav: import("@angular/core").WritableSignal<NaviGraphNode[]>;
1867
+ /** Resolved root nodes for navi.admin.topbar. */
1868
+ readonly topbarNav: import("@angular/core").WritableSignal<NaviGraphNode[]>;
1869
+ private readonly cache;
1870
+ /**
1871
+ * `fresh` asks the server again instead of answering from this service's cache: the answer is per account AND
1872
+ * per elevation state (a node is 'elevate' before a grant and 'allowed' after it), so a change of elevation must
1873
+ * re-ask, or a lock would outlive the grant -- or come back only after a reload.
1874
+ */
1875
+ loadAdminNav(url: string, options?: {
1876
+ fresh?: boolean;
1877
+ }): Observable<NaviGraphNode[]>;
1878
+ loadTopbarNav(url: string): Observable<NaviGraphNode[]>;
1879
+ /**
1880
+ * Handle a nav-node click.
1881
+ *
1882
+ * Routing logic based on meta.target:
1883
+ * 'action.logout' -- dispatch Logout
1884
+ * '_blank' -- window.open (uses node.path as href)
1885
+ * default -- router.navigate with meta.routerLink or node.path
1886
+ */
1887
+ handleClick(node: NaviGraphNode): void;
1888
+ /**
1889
+ * Evaluate `showWhen` meta condition against a record.
1890
+ * Nodes with no `showWhen` are always visible.
1891
+ * Supports nested AND/OR logic: { and: [...] } / { or: [...] }
1892
+ */
1893
+ isVisible(node: NaviGraphNode, record: Record<string, unknown>): boolean;
1894
+ /**
1895
+ * Standalone `showWhen` evaluator for callers that don't have a
1896
+ * full `NaviGraphNode` (e.g. a grid's row context menu -- a
1897
+ * `rowAction.showWhen` predicate is evaluated against the row's
1898
+ * own projection). `null`/`undefined` predicates default to
1899
+ * visible, mirroring `isVisible`'s "no rule = always shown"
1900
+ * semantics.
1901
+ */
1902
+ matchesShowWhen(showWhen: Record<string, unknown> | undefined | null, record: Record<string, unknown>): boolean;
1903
+ private evalCondition;
1904
+ /**
1905
+ * Fetch a graph endpoint and return a nested tree.
1906
+ * Handles both compact JSON-LD IRIs (`member`) and
1907
+ * the legacy Hydra key (`hydra:member`).
1908
+ * Results are cached by URL for the lifetime of the service instance.
1909
+ */
1910
+ loadTree(url: string, fresh?: boolean): Observable<NaviGraphNode[]>;
1911
+ /**
1912
+ * Build a nested tree from a flat array of nodes.
1913
+ * Nodes are sorted by sortOrder at each level.
1914
+ */
1915
+ private buildTree;
1916
+ static ɵfac: i0.ɵɵFactoryDeclaration<NaviGraphService, never>;
1917
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1918
+ }
1885
1919
  /**
1886
1920
  * Registry mapping string keys to Angular component classes.
1887
1921
  *
@@ -1895,20 +1929,19 @@ declare class NaviGraphService {
1895
1929
  * private readonly registry = inject(ComponentRegistry);
1896
1930
  * const cls = this.registry.get('MediaLibraryPage');
1897
1931
  */
1898
- declare class ComponentRegistry {
1899
- private static readonly map;
1900
- static register(key: string, component: Type<unknown>): void;
1901
- static get(key: string): Type<unknown> | undefined;
1902
- static has(key: string): boolean;
1903
- static keys(): string[];
1904
- register(key: string, component: Type<unknown>): void;
1905
- get(key: string): Type<unknown> | null;
1906
- has(key: string): boolean;
1907
- keys(): string[];
1908
- static ɵfac: i0.ɵɵFactoryDeclaration<ComponentRegistry, never>;
1909
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1910
- }
1911
-
1932
+ export declare class ComponentRegistry {
1933
+ private static readonly map;
1934
+ static register(key: string, component: Type<unknown>): void;
1935
+ static get(key: string): Type<unknown> | undefined;
1936
+ static has(key: string): boolean;
1937
+ static keys(): string[];
1938
+ register(key: string, component: Type<unknown>): void;
1939
+ get(key: string): Type<unknown> | null;
1940
+ has(key: string): boolean;
1941
+ keys(): string[];
1942
+ static ɵfac: i0.ɵɵFactoryDeclaration<ComponentRegistry, never>;
1943
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1944
+ }
1912
1945
  /**
1913
1946
  * `console` -- the administration host contract (the platform rule: hosts implement contracts, modules offer entries).
1914
1947
  *
@@ -1924,35 +1957,35 @@ declare class ComponentRegistry {
1924
1957
  * (`^1.2` -- major 1, minor at least 2), checked at build against this
1925
1958
  * constant and at install against the theme's declaration.
1926
1959
  */
1927
- declare const CONSOLE_CONTRACT: {
1928
- readonly name: "console";
1929
- readonly version: "1.0";
1960
+ export declare const CONSOLE_CONTRACT: {
1961
+ readonly name: "console";
1962
+ readonly version: "1.0";
1930
1963
  };
1931
- interface ConsoleEntry {
1932
- /** The backend module id, as `config/modules/<id>`. Activation is by this id. */
1933
- readonly module: string;
1934
- readonly contract: 'console';
1935
- /** The console versions this entry was written for: a caret range over MAJOR.MINOR, e.g. '^1.0'. */
1936
- readonly range: string;
1937
- readonly routes?: readonly ConsoleRouteMount[];
1938
- readonly bindings?: readonly ConsoleBinding[];
1939
- readonly topbar?: readonly ConsoleTopbarItem[];
1940
- readonly overlays?: readonly ConsoleOverlay[];
1941
- readonly panels?: readonly ConsolePanel[];
1942
- readonly states?: readonly Type<unknown>[];
1943
- readonly providers?: readonly ConsoleProvision[];
1964
+ export interface ConsoleEntry {
1965
+ /** The backend module id, as `config/modules/<id>`. Activation is by this id. */
1966
+ readonly module: string;
1967
+ readonly contract: 'console';
1968
+ /** The console versions this entry was written for: a caret range over MAJOR.MINOR, e.g. '^1.0'. */
1969
+ readonly range: string;
1970
+ readonly routes?: readonly ConsoleRouteMount[];
1971
+ readonly bindings?: readonly ConsoleBinding[];
1972
+ readonly topbar?: readonly ConsoleTopbarItem[];
1973
+ readonly overlays?: readonly ConsoleOverlay[];
1974
+ readonly panels?: readonly ConsolePanel[];
1975
+ readonly states?: readonly Type<unknown>[];
1976
+ readonly providers?: readonly ConsoleProvision[];
1944
1977
  }
1945
1978
  /** What a route's `data` carries for the shell: the sidebar item to light, and whether the page owns its height. */
1946
- interface ConsoleNavHint {
1947
- readonly activeNav?: string;
1948
- readonly fullHeight?: boolean;
1949
- /** An intermediate crumb the top bar renders between Home and the page. */
1950
- readonly breadcrumb?: {
1951
- readonly label: string;
1952
- readonly routerLink: string;
1953
- };
1954
- /** Which server layout the page renders, for the generic layout pages. */
1955
- readonly layoutId?: string;
1979
+ export interface ConsoleNavHint {
1980
+ readonly activeNav?: string;
1981
+ readonly fullHeight?: boolean;
1982
+ /** An intermediate crumb the top bar renders between Home and the page. */
1983
+ readonly breadcrumb?: {
1984
+ readonly label: string;
1985
+ readonly routerLink: string;
1986
+ };
1987
+ /** Which server layout the page renders, for the generic layout pages. */
1988
+ readonly layoutId?: string;
1956
1989
  }
1957
1990
  /**
1958
1991
  * A route mounted under the console's authenticated layout. The host adds
@@ -1960,52 +1993,52 @@ interface ConsoleNavHint {
1960
1993
  * and a `canMatch` that answers whether the module is installed here. Routes
1961
1994
  * are lazy: a static component here would put every module's pages in `main`.
1962
1995
  */
1963
- type ConsoleRouteMount = {
1964
- readonly path: string;
1965
- readonly children: () => Promise<Routes>;
1966
- readonly nav?: ConsoleNavHint;
1967
- readonly providers?: readonly Provider[];
1996
+ export type ConsoleRouteMount = {
1997
+ readonly path: string;
1998
+ readonly children: () => Promise<Routes>;
1999
+ readonly nav?: ConsoleNavHint;
2000
+ readonly providers?: readonly Provider[];
1968
2001
  } | {
1969
- readonly path: string;
1970
- readonly component: () => Promise<Type<unknown>>;
1971
- readonly nav?: ConsoleNavHint;
1972
- readonly providers?: readonly Provider[];
2002
+ readonly path: string;
2003
+ readonly component: () => Promise<Type<unknown>>;
2004
+ readonly nav?: ConsoleNavHint;
2005
+ readonly providers?: readonly Provider[];
1973
2006
  } | {
1974
- readonly path: string;
1975
- readonly redirectTo: string;
2007
+ readonly path: string;
2008
+ readonly redirectTo: string;
1976
2009
  };
1977
2010
  /** A `ComponentRegistry` name -> class: names a module OWNS (its server layouts name them) and names it fills in another module's slot (`profile.tab:call`). */
1978
- interface ConsoleBinding {
1979
- readonly name: string;
1980
- readonly component: Type<unknown>;
2011
+ export interface ConsoleBinding {
2012
+ readonly name: string;
2013
+ readonly component: Type<unknown>;
1981
2014
  }
1982
2015
  /** A tile in the top bar's quick-access strip, rendered in ascending `order`. */
1983
- interface ConsoleTopbarItem {
1984
- readonly id: string;
1985
- readonly order: number;
1986
- readonly component: Type<unknown>;
2016
+ export interface ConsoleTopbarItem {
2017
+ readonly id: string;
2018
+ readonly order: number;
2019
+ readonly component: Type<unknown>;
1987
2020
  }
1988
2021
  /** Mounted once, inside the authenticated layout, outside the router outlet. */
1989
- interface ConsoleOverlay {
1990
- readonly id: string;
1991
- readonly component: Type<unknown>;
2022
+ export interface ConsoleOverlay {
2023
+ readonly id: string;
2024
+ readonly component: Type<unknown>;
1992
2025
  }
1993
2026
  /**
1994
2027
  * A dock panel the layout opens and closes: the host renders its toggle in
1995
2028
  * the top bar and the panel in the dock, and the panel talks back through
1996
2029
  * {@link ConsolePanelHost} -- not through outputs, which a slot cannot carry.
1997
2030
  */
1998
- interface ConsolePanel {
1999
- readonly id: string;
2000
- readonly dock: 'bottom';
2001
- /** The top-bar toggle: a label (the title), an icon name or a short text, and a key the host binds with Ctrl. */
2002
- readonly toggle: {
2003
- readonly label: string;
2004
- readonly icon?: string;
2005
- readonly text?: string;
2006
- readonly key?: string;
2007
- };
2008
- readonly component: Type<unknown>;
2031
+ export interface ConsolePanel {
2032
+ readonly id: string;
2033
+ readonly dock: 'bottom';
2034
+ /** The top-bar toggle: a label (the title), an icon name or a short text, and a key the host binds with Ctrl. */
2035
+ readonly toggle: {
2036
+ readonly label: string;
2037
+ readonly icon?: string;
2038
+ readonly text?: string;
2039
+ readonly key?: string;
2040
+ };
2041
+ readonly component: Type<unknown>;
2009
2042
  }
2010
2043
  /**
2011
2044
  * What a `provide*` line is: environment providers from a library's registry
@@ -2013,24 +2046,24 @@ interface ConsolePanel {
2013
2046
  * implementation of a single-implementation port, listed as such so the
2014
2047
  * assembly can refuse two modules implementing one port by name.
2015
2048
  */
2016
- type ConsoleProvision = EnvironmentProviders | Provider | ConsolePortProvision;
2017
- interface ConsolePortProvision {
2018
- readonly port: InjectionToken<unknown>;
2019
- readonly useExisting: Type<unknown>;
2049
+ export type ConsoleProvision = EnvironmentProviders | Provider | ConsolePortProvision;
2050
+ export interface ConsolePortProvision {
2051
+ readonly port: InjectionToken<unknown>;
2052
+ readonly useExisting: Type<unknown>;
2020
2053
  }
2021
- declare function isPortProvision(p: ConsoleProvision): p is ConsolePortProvision;
2054
+ export declare function isPortProvision(p: ConsoleProvision): p is ConsolePortProvision;
2022
2055
  /** The identity function with the type: an entry file exports data, nothing runs. */
2023
- declare function consoleEntry(entry: ConsoleEntry): ConsoleEntry;
2056
+ export declare function consoleEntry(entry: ConsoleEntry): ConsoleEntry;
2024
2057
  /**
2025
2058
  * Whether a caret range over MAJOR.MINOR admits a version: same major, minor
2026
2059
  * at least the range's. Malformed input admits nothing -- a range that cannot
2027
2060
  * be read must refuse, never pass.
2028
2061
  */
2029
- declare function satisfiesRange(range: string, version: string): boolean;
2062
+ export declare function satisfiesRange(range: string, version: string): boolean;
2030
2063
  /** The refusal an assembly raises: one message per collision, all of them, by name. */
2031
- declare class ConsoleAssemblyError extends Error {
2032
- readonly problems: readonly string[];
2033
- constructor(problems: readonly string[]);
2064
+ export declare class ConsoleAssemblyError extends Error {
2065
+ readonly problems: readonly string[];
2066
+ constructor(problems: readonly string[]);
2034
2067
  }
2035
2068
  /**
2036
2069
  * The checks every assembly runs, in one place so the build-time spec, the
@@ -2038,8 +2071,7 @@ declare class ConsoleAssemblyError extends Error {
2038
2071
  * this contract, and the collision rules of the six lists. Returns the
2039
2072
  * entries unchanged when nothing is wrong.
2040
2073
  */
2041
- declare function assembleConsole(entries: readonly ConsoleEntry[]): readonly ConsoleEntry[];
2042
-
2074
+ export declare function assembleConsole(entries: readonly ConsoleEntry[]): readonly ConsoleEntry[];
2043
2075
  /**
2044
2076
  * The host side of `console@1`: what a theme calls to turn the assembled
2045
2077
  * entries into routes, providers and the lists its shell renders.
@@ -2052,31 +2084,31 @@ declare function assembleConsole(entries: readonly ConsoleEntry[]): readonly Con
2052
2084
  * silently absent.
2053
2085
  */
2054
2086
  /** Every entry this build assembled, after {@link assembleConsole}'s checks. */
2055
- declare const CONSOLE_ENTRIES: InjectionToken<readonly ConsoleEntry[]>;
2087
+ export declare const CONSOLE_ENTRIES: InjectionToken<readonly ConsoleEntry[]>;
2056
2088
  /**
2057
2089
  * The port a dock panel talks back through: the layout provides one per
2058
2090
  * panel it mounts, so the panel never needs an output the slot cannot carry.
2059
2091
  */
2060
- declare abstract class ConsolePanelHost {
2061
- abstract close(): void;
2062
- abstract maximize(on: boolean): void;
2092
+ export declare abstract class ConsolePanelHost {
2093
+ abstract close(): void;
2094
+ abstract maximize(on: boolean): void;
2063
2095
  }
2064
2096
  /** The console modules the manifest says are installed here. */
2065
- declare function installedConsoleModules(manifest: ApiManifest | null): ReadonlySet<string>;
2066
- declare class ConsoleActivation {
2067
- private readonly entries;
2068
- private readonly manifest;
2069
- readonly installed: i0.Signal<ReadonlySet<string>>;
2070
- /** The entries of installed modules, in assembly order. */
2071
- readonly active: i0.Signal<ConsoleEntry[]>;
2072
- readonly topbar: i0.Signal<readonly ConsoleTopbarItem[]>;
2073
- readonly overlays: i0.Signal<readonly ConsoleOverlay[]>;
2074
- readonly panels: i0.Signal<readonly ConsolePanel[]>;
2075
- /** Modules the manifest names that this build has no entry for: what a diagnostics page shows by name. */
2076
- readonly missing: i0.Signal<string[]>;
2077
- isInstalled(module: string): boolean;
2078
- static ɵfac: i0.ɵɵFactoryDeclaration<ConsoleActivation, never>;
2079
- static ɵprov: i0.ɵɵInjectableDeclaration<any>;
2097
+ export declare function installedConsoleModules(manifest: ApiManifest | null): ReadonlySet<string>;
2098
+ export declare class ConsoleActivation {
2099
+ private readonly entries;
2100
+ private readonly manifest;
2101
+ readonly installed: import("@angular/core").Signal<ReadonlySet<string>>;
2102
+ /** The entries of installed modules, in assembly order. */
2103
+ readonly active: import("@angular/core").Signal<ConsoleEntry[]>;
2104
+ readonly topbar: import("@angular/core").Signal<readonly ConsoleTopbarItem[]>;
2105
+ readonly overlays: import("@angular/core").Signal<readonly ConsoleOverlay[]>;
2106
+ readonly panels: import("@angular/core").Signal<readonly ConsolePanel[]>;
2107
+ /** Modules the manifest names that this build has no entry for: what a diagnostics page shows by name. */
2108
+ readonly missing: import("@angular/core").Signal<string[]>;
2109
+ isInstalled(module: string): boolean;
2110
+ static ɵfac: i0.ɵɵFactoryDeclaration<ConsoleActivation, never>;
2111
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
2080
2112
  }
2081
2113
  /**
2082
2114
  * `canMatch` for a module's mount: the route exists only where the module is
@@ -2096,7 +2128,7 @@ declare class ConsoleActivation {
2096
2128
  * nothing is asked, the answer is the public config's, and the layout's guard
2097
2129
  * sends the person to sign in. A refusal merges nothing, so the answer is no.
2098
2130
  */
2099
- declare function consoleModuleInstalled(module: string): CanMatchFn;
2131
+ export declare function consoleModuleInstalled(module: string): CanMatchFn;
2100
2132
  /**
2101
2133
  * The children a theme mounts under its authenticated layout for the
2102
2134
  * assembled entries: one `Route` per mount, lazy as written, `data` from the
@@ -2104,7 +2136,7 @@ declare function consoleModuleInstalled(module: string): CanMatchFn;
2104
2136
  * shape around them (login, the layout, `canActivate: [authGuard]`, the
2105
2137
  * wildcard).
2106
2138
  */
2107
- declare function consoleChildren(entries: readonly ConsoleEntry[]): Routes;
2139
+ export declare function consoleChildren(entries: readonly ConsoleEntry[]): Routes;
2108
2140
  /**
2109
2141
  * The providers a theme adds for the assembled entries: the store with the
2110
2142
  * host's states and every module state, the registry bindings (an
@@ -2113,10 +2145,9 @@ declare function consoleChildren(entries: readonly ConsoleEntry[]): Routes;
2113
2145
  * library providers a module's provisions extend (the editor bridge, the
2114
2146
  * PDF viewer), as the hand-written lines were.
2115
2147
  */
2116
- declare function provideConsole(entries: readonly ConsoleEntry[], options: {
2117
- readonly hostStates: readonly Type<unknown>[];
2148
+ export declare function provideConsole(entries: readonly ConsoleEntry[], options: {
2149
+ readonly hostStates: readonly Type<unknown>[];
2118
2150
  }): EnvironmentProviders;
2119
-
2120
2151
  /**
2121
2152
  * Phase H7 -- admin Site Selector.
2122
2153
  *
@@ -2146,8 +2177,7 @@ declare function provideConsole(entries: readonly ConsoleEntry[], options: {
2146
2177
  * be ignored by the backend anyway, but skipping keeps wire traffic clean)
2147
2178
  * - requests that already carry the header (programmatic overrides win)
2148
2179
  */
2149
- declare const sectionInterceptor: HttpInterceptorFn;
2150
-
2180
+ export declare const sectionInterceptor: HttpInterceptorFn;
2151
2181
  /**
2152
2182
  * The one fact the section interceptor needs: which site the operator is
2153
2183
  * currently working in.
@@ -2162,16 +2192,13 @@ declare const sectionInterceptor: HttpInterceptorFn;
2162
2192
  * Read SYNCHRONOUSLY on purpose. An interceptor decides whether to stamp a
2163
2193
  * header while building the request; it cannot wait on a stream.
2164
2194
  */
2165
- interface CurrentSectionPort {
2166
- /** Slug of the active section, or null when the operator picked none. */
2167
- currentSlug(): string | null;
2195
+ export interface CurrentSectionPort {
2196
+ /** Slug of the active section, or null when the operator picked none. */
2197
+ currentSlug(): string | null;
2168
2198
  }
2169
2199
  /**
2170
2200
  * Optional by design: an application assembled without a Sections module binds
2171
2201
  * nothing and simply sends no `X-CoolMS-Section` header, which is exactly what
2172
2202
  * a single-site install wants.
2173
2203
  */
2174
- declare const CURRENT_SECTION: InjectionToken<CurrentSectionPort>;
2175
-
2176
- export { AppConfigState, AppInitService, AuthRefreshCoordinator, AuthState, BACKGROUND_REQUEST, BYPASS_AUTH, BYPASS_ELEVATION, CONSOLE_CONTRACT, CONSOLE_ENTRIES, CURRENT_SECTION, CmsLoaderComponent, ComponentRegistry, ConfigService, ConsoleAccessService, ConsoleActivation, ConsoleAssemblyError, ConsolePanelHost, CrossTabAuthSyncService, ELEVATION_NOTICE, ELEVATION_PROMPT, ELEVATION_REQUIRED_HEADER, ELEVATION_WARNING_FACTOR_MISSING, ELEVATION_WARNING_MFA_OFF, ELEVATION_WARNING_PASSWORD_NOT_SET, ElevationService, EntitySearchService, ErrorHandlerService, IdentityApiClient, Login, LoginComponent, Logout, NaviGraphService, PatchCurrentUser, RealtimeTokenClient, RestoreSession, SetAppConfig, SetTokens, SidebarStateService, ThemeService, UserPreferencesService, assembleConsole, authGuard, authInterceptor, consoleChildren, consoleEntry, consoleModuleInstalled, elevationInterceptor, installedConsoleModules, isElevationRefusal, isPortProvision, loginPageGuard, provideConsole, resolvePattern, satisfiesRange, sectionInterceptor };
2177
- export type { ApiManifest, AppConfigStateModel, AuthApiManifest, AuthStateModel, CentrifugoConnectionTokenDto, CentrifugoSubscriptionTokenDto, ConsoleAccess, ConsoleBinding, ConsoleEntry, ConsoleManifestResponse, ConsoleNavHint, ConsoleOverlay, ConsolePanel, ConsolePortProvision, ConsoleProvision, ConsoleRouteMount, ConsoleTopbarItem, ContentApiManifest, CurrentSectionPort, DataGridApiManifest, DataGridPreference, DataGridPreferences, DataSourceDefinition, DataSourceLoading, DataSourceOption, DataSourceType, DataSourceWidget, DatagridConfig, DialogConfig, DocumentApiManifest, DomainExplorerApiManifest, DynamicEntityApiManifest, EditorApiManifest, EditorApiManifestEntry, EditorApiManifestProfile, ElevationChange, ElevationChangeKind, ElevationDropReason, ElevationEnded, ElevationEndedReason, ElevationNoticePort, ElevationPromptPort, ElevationPromptRequest, ElevationState, ElevationWarning, EntitySearchResult, FieldItem, FieldSecurityPolicy, FieldType, FormAction, FormActionType, FormActionVariant, FormConfig, FormRenderDefinition, HydraCollection, HydraView, IdentityApiManifest, LayoutConfig, LayoutHeaderAction, LayoutNode, LayoutSection, MediaApiManifest, NaviApiManifest, NaviGraphMeta, NaviGraphNode, NaviGraphNodeFlat, NaviGraphTarget, NavigraphConfig, PlatformDefaults, RelationDefinition, ResolvedTheme, SectionApiManifest, SubFormDefinition, TerminalApiManifest, ThemeChoice, ThemeConfigResponse, TokenResponse, UiApiManifest, UiModuleEntryManifest, UserDto, ValidatorDefinition, VfsApiManifest, ViewerApiManifest, ViewerDefinitionApiManifest, ViewerProfileApiManifest, VisibilityCondition, VisibilityOperator };
2204
+ export declare const CURRENT_SECTION: InjectionToken<CurrentSectionPort>;