@yuuvis/client-framework 3.4.2 → 3.6.0

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,5 +1,215 @@
1
1
  # @yuuvis/client-framework/renderer
2
2
 
3
- Secondary entry point of `@yuuvis/client-framework`. It can be used by importing from `@yuuvis/client-framework/renderer`.
3
+ Secondary entry point of `@yuuvis/client-framework`. Import everything from `@yuuvis/client-framework/renderer`.
4
4
 
5
- Provides default renderers for object type properties. Also comes with the `renderer.service` that allows to register custom renderers.
5
+ Provides a pluggable rendering layer for two distinct things:
6
+
7
+ 1. **Property renderers** — components that render a single object-type property value (string, integer, datetime, file size, organization, table, …) inside list rows, summary panels, tile slots, smart-search results, and detail headers.
8
+ 2. **Audit renderers** — components that render the inner content of a single entry in the audit timeline (`ObjectAuditComponent`), keyed by the audit `action` (and optionally `subaction`).
9
+
10
+ Both subsystems share the same shape: a service that maintains a registry, an abstract base component that consumers extend, and a directive that resolves and instantiates the right component for a given input.
11
+
12
+ ## Public API
13
+
14
+ | Export | Kind | Purpose |
15
+ |---|---|---|
16
+ | `RendererService` | service (`providedIn: 'root'`) | Registry for property renderers. |
17
+ | `RendererDirective` (`*yuvRenderer`) | structural directive | Renders a `ResolvedObjectConfigItem` using the registered renderer. |
18
+ | `AbstractRendererComponent<T, U>` | abstract component | Base class for custom property renderers. |
19
+ | `RendererComponent` | type | Union of the built-in property renderer components. |
20
+ | `RendererDirectiveInput` | type alias of `ResolvedObjectConfigItem` | Input shape for `*yuvRenderer`. |
21
+ | `AuditRendererService` | service (`providedIn: 'root'`) | Registry for audit-entry renderers. |
22
+ | `AuditRendererDirective` (`[yuvAuditRenderer]`) | attribute directive | Renders an `AuditEntry` using the registered renderer. |
23
+ | `AbstractAuditRendererComponent` | abstract component | Base class for custom audit renderers. |
24
+ | `DefaultAuditRendererComponent` | component | Built-in fallback that resolves localized audit labels for all known actions (100, 200, 300, 400, custom 10000). |
25
+ | Built-in property renderers | components | `StringRendererComponent`, `IntegerRendererComponent`, `DecimalRendererComponent`, `BooleanRendererComponent`, `DateTimeRendererComponent`, `FileSizeRendererComponent`, `IconRendererComponent`, `OrganizationRendererComponent`, `TableRendererComponent`, `UnknownRendererComponent` (fallback). |
26
+
27
+ ---
28
+
29
+ ## Property renderers
30
+
31
+ ### Rendering a property — `*yuvRenderer`
32
+
33
+ `*yuvRenderer` takes a `ResolvedObjectConfigItem` and instantiates the matching renderer into the host view. The host owns the layout; the directive owns the value cell.
34
+
35
+ ```html
36
+ @for (property of object.properties; track property.propertyName) {
37
+ <div class="row">
38
+ <div class="label">{{ property.label }}</div>
39
+ <div class="value">
40
+ <ng-container *yuvRenderer="property.value" />
41
+ </div>
42
+ </div>
43
+ }
44
+ ```
45
+
46
+ Input shape:
47
+
48
+ ```ts
49
+ interface ResolvedObjectConfigItem {
50
+ propertyName: string; // schema property name (e.g. 'system:contentStreamLength')
51
+ rendererType?: RendererType; // optional explicit renderer key — wins over propertyName lookup
52
+ value?: unknown; // the value passed to the renderer
53
+ meta?: Record<string, unknown>; // additional context (e.g. *_title for user fields)
54
+ }
55
+ ```
56
+
57
+ Resolution order inside `RendererService`:
58
+
59
+ 1. If `rendererType` is set → look up by that key (`getRendererByType`).
60
+ 2. Otherwise look up by `propertyName` (`getRenderer`). If not registered, fall back to the property's internal field type derived from `SystemService`.
61
+ 3. If nothing matches → `UnknownRendererComponent`.
62
+
63
+ The directive re-renders only when the input object actually changes (deep equality via `JSON.stringify`).
64
+
65
+ ### Built-in registrations
66
+
67
+ `RendererService` ships with these defaults (in `RendererService` constructor):
68
+
69
+ | Key | Component |
70
+ |---|---|
71
+ | `integer` | `IntegerRendererComponent` |
72
+ | `decimal` | `DecimalRendererComponent` |
73
+ | `datetime` | `DateTimeRendererComponent` |
74
+ | `icon` | `IconRendererComponent` |
75
+ | `string` | `StringRendererComponent` |
76
+ | `boolean` | `BooleanRendererComponent` |
77
+ | `table` | `TableRendererComponent` |
78
+ | `InternalFieldType.STRING_ORGANIZATION` / `STRING_ORGANIZATION_SET` | `OrganizationRendererComponent` |
79
+ | `ContentStreamField.LENGTH` | `FileSizeRendererComponent` |
80
+
81
+ ### Registering a custom renderer
82
+
83
+ Get the service and call one of the registration methods. Either overrides the default for that key.
84
+
85
+ ```ts
86
+ import { inject, Injectable } from '@angular/core';
87
+ import { RendererService } from '@yuuvis/client-framework/renderer';
88
+ import { MyHashRendererComponent } from './my-hash.renderer';
89
+
90
+ @Injectable({ providedIn: 'root' })
91
+ export class MyAppRendererSetup {
92
+ #renderers = inject(RendererService);
93
+
94
+ init(): void {
95
+ // Override by renderer type (matches `rendererType` on ResolvedObjectConfigItem)
96
+ this.#renderers.registerRendererByType('string', MyHashRendererComponent);
97
+
98
+ // Or scope to a single property — and optionally to a specific object type
99
+ this.#renderers.registerRenderer(MyHashRendererComponent, 'document:sha256');
100
+ this.#renderers.registerRenderer(MyHashRendererComponent, 'document:sha256', 'system:document');
101
+ }
102
+ }
103
+ ```
104
+
105
+ Call this from `APP_INITIALIZER` or any service that runs before the views using the directive are constructed. Registrations are stored in a signal, so consumers that re-resolve their renderer will pick up changes; however the directive only re-resolves when its input changes — switching renderers at runtime requires re-pushing the input.
106
+
107
+ ### Writing a custom property renderer
108
+
109
+ Extend `AbstractRendererComponent<T, U>`. `T` is the value type, `U` is the meta-shape (defaults to `null`).
110
+
111
+ ```ts
112
+ import { ChangeDetectionStrategy, Component, computed } from '@angular/core';
113
+ import { AbstractRendererComponent } from '@yuuvis/client-framework/renderer';
114
+
115
+ @Component({
116
+ selector: 'app-hash-renderer',
117
+ standalone: true,
118
+ template: `<code class="hash" [title]="value()">{{ short() }}</code>`,
119
+ changeDetection: ChangeDetectionStrategy.OnPush
120
+ })
121
+ export class MyHashRendererComponent extends AbstractRendererComponent<string> {
122
+ protected short = computed(() => this.value()?.slice(0, 12) ?? '');
123
+ }
124
+ ```
125
+
126
+ The base class exposes:
127
+
128
+ - `propertyName = input.required<string>()`
129
+ - `value = input.required<T | null>()`
130
+ - `meta = input<Record<string, unknown> | U>()`
131
+ - `getProperty(): SchemaResponseFieldDefinition | undefined` — schema definition for the property from `SystemService`.
132
+
133
+ All renderers must be `standalone: true` and should use `ChangeDetectionStrategy.OnPush`.
134
+
135
+ ---
136
+
137
+ ## Audit renderers
138
+
139
+ ### Rendering an audit entry — `[yuvAuditRenderer]`
140
+
141
+ `[yuvAuditRenderer]` is the attribute-directive twin for the audit timeline. The host component (`ObjectAuditComponent`) owns the date column, timeline line, version badge, and creator label; the renderer owns the *inner content* of the entry.
142
+
143
+ ```html
144
+ @for (item of items(); track item.creationDate + '-' + item.action) {
145
+ <li class="audit">
146
+ <time>{{ item.creationDate | localeDate }}</time>
147
+ <div class="content"><ng-container [yuvAuditRenderer]="item" /></div>
148
+ <span class="version">v{{ item.version }}</span>
149
+ <div class="creator">{{ item.createdBy.title }}</div>
150
+ </li>
151
+ }
152
+ ```
153
+
154
+ Resolution order inside `AuditRendererService.getAuditRenderer(action, subaction)`:
155
+
156
+ 1. `(action, subaction)` — if `subaction` is provided and a renderer is registered for the exact pair.
157
+ 2. `(action)` — action-only registration.
158
+ 3. `DefaultAuditRendererComponent` — built-in fallback that translates the well-known audit labels (`yuv.audit.label.*`) for actions 100/200/300/400 and custom action 10000.
159
+
160
+ ### Registering a custom audit renderer
161
+
162
+ ```ts
163
+ import { inject, Injectable } from '@angular/core';
164
+ import { AuditRendererService } from '@yuuvis/client-framework/renderer';
165
+ import { CreatedAuditRendererComponent } from './created-audit.renderer';
166
+ import { UpdatedAuditRendererComponent } from './updated-audit.renderer';
167
+
168
+ @Injectable({ providedIn: 'root' })
169
+ export class MyAppAuditSetup {
170
+ #audit = inject(AuditRendererService);
171
+
172
+ init(): void {
173
+ // Override every CREATE_METADATA (action 100) entry
174
+ this.#audit.registerAuditRenderer(CreatedAuditRendererComponent, 100);
175
+
176
+ // Override one specific (action, subaction) pair
177
+ this.#audit.registerAuditRenderer(UpdatedAuditRendererComponent, 300, 42);
178
+ }
179
+ }
180
+ ```
181
+
182
+ ### Writing a custom audit renderer
183
+
184
+ Extend `AbstractAuditRendererComponent`. The directive sets one input: `auditEntry: AuditEntry`.
185
+
186
+ ```ts
187
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
188
+ import { AbstractAuditRendererComponent } from '@yuuvis/client-framework/renderer';
189
+
190
+ @Component({
191
+ selector: 'app-created-audit',
192
+ standalone: true,
193
+ template: `
194
+ <span class="title">📄 Document created</span>
195
+ <small class="meta">action {{ auditEntry().action }}</small>
196
+ `,
197
+ changeDetection: ChangeDetectionStrategy.OnPush
198
+ })
199
+ export class CreatedAuditRendererComponent extends AbstractAuditRendererComponent {}
200
+ ```
201
+
202
+ The base class exposes:
203
+
204
+ - `auditEntry = input.required<AuditEntry>()` — the full entry, including `action`, `subaction`, `detail`, `creationDate`, `createdBy`, `version`.
205
+
206
+ If you want to extend (rather than replace) the default labelling, you can compose `DefaultAuditRendererComponent` inside your own template, or read `auditEntry().detail` directly — its format is action-specific (the default renderer parses `[a,b]`-style payloads for tag/restore actions).
207
+
208
+ ---
209
+
210
+ ## Conventions
211
+
212
+ - Both directives, both services, and both abstract base classes are tree-shakable per sub-library — import only what you need.
213
+ - Renderers are looked up at directive instantiation time. Registering after the view exists is fine but only affects later input changes.
214
+ - All built-in renderers use OnPush and are standalone — follow the same in custom renderers.
215
+ - `RendererService` integrates with `SystemService` to fall back to the property's internal field type; custom renderers do not need to replicate this — register against the field-type key (`registerRendererByType`) to opt in.
@@ -1,7 +1,7 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { TemplateRef, OnDestroy, PipeTransform } from '@angular/core';
3
3
  import * as _yuuvis_client_core from '@yuuvis/client-core';
4
- import { AuditQueryResult, AuditEntry, DmsObject, AuditService, EventService, SystemService, TranslateService, VirtualObjectType, RetentionState, ObjectTypeFlavor, Situation, FlavoredDmsObject, ObjectTypeField } from '@yuuvis/client-core';
4
+ import { AuditQueryResult, AuditEntry, DmsObject, VirtualObjectType, RetentionState, TranslateService, ObjectTypeFlavor, Situation, FlavoredDmsObject, ObjectTypeField } from '@yuuvis/client-core';
5
5
  import { RendererDirectiveInput } from '@yuuvis/client-framework/renderer';
6
6
  import { ObjectFormComponent, IObjectFormElementExtension, ObjectFormOptions, FormStatusChangedEvent } from '@yuuvis/client-framework/object-form';
7
7
  import * as i7 from '@yuuvis/client-framework/common';
@@ -9,26 +9,14 @@ import { FormGroup } from '@angular/forms';
9
9
 
10
10
  declare class ObjectAuditComponent {
11
11
  #private;
12
- private auditService;
13
- private eventService;
14
- private system;
15
- private translate;
16
- private FILTER_CACHE_KEY;
17
- private _objectID?;
18
- private _objectTypeID?;
19
12
  icons: {
20
13
  filter: any;
21
14
  arrowNext: any;
22
15
  };
23
16
  auditsRes: _angular_core.WritableSignal<AuditQueryResult | undefined>;
24
- resolvedItems: _angular_core.WritableSignal<ResolvedAuditEntry[]>;
17
+ items: _angular_core.Signal<AuditEntry[]>;
25
18
  error: _angular_core.WritableSignal<boolean>;
26
19
  busy: _angular_core.WritableSignal<boolean>;
27
- searchActions: {
28
- label: string;
29
- actions: string[];
30
- }[];
31
- auditLabels: any;
32
20
  dmsObject: _angular_core.InputSignal<DmsObject | undefined>;
33
21
  /**
34
22
  * A list of audits that should not be shown. Use the audit codes (like 100, 301, etc.).
@@ -40,19 +28,15 @@ declare class ObjectAuditComponent {
40
28
  */
41
29
  allActions: _angular_core.InputSignal<boolean>;
42
30
  get objectID(): string | undefined;
43
- constructor(auditService: AuditService, eventService: EventService, system: SystemService, translate: TranslateService);
31
+ constructor();
44
32
  /**
45
33
  * Execute a query from the search panel.
46
34
  */
47
35
  query(): void;
48
36
  goToPage(page: number): void;
49
- private onError;
50
37
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<ObjectAuditComponent, never>;
51
38
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ObjectAuditComponent, "yuv-object-audit", never, { "dmsObject": { "alias": "dmsObject"; "required": false; "isSignal": true; }; "skipActions": { "alias": "skipActions"; "required": false; "isSignal": true; }; "allActions": { "alias": "allActions"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
52
39
  }
53
- interface ResolvedAuditEntry extends AuditEntry {
54
- label: string;
55
- }
56
40
 
57
41
  interface HeaderData {
58
42
  title: RendererDirectiveInput;
@@ -1,8 +1,68 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Type, ComponentRef } from '@angular/core';
3
- import { SchemaResponseFieldDefinition, TranslateService, ResolvedObjectConfigItem, RendererType } from '@yuuvis/client-core';
2
+ import { ComponentRef, Type } from '@angular/core';
3
+ import { AuditEntry, SchemaResponseFieldDefinition, TranslateService, ResolvedObjectConfigItem, RendererType } from '@yuuvis/client-core';
4
4
  import * as dist_libs_yuuvis_client_core_types_yuuvis_client_core from 'dist/libs/yuuvis/client-core/types/yuuvis-client-core';
5
5
 
6
+ /**
7
+ * Abstract class to be extended by audit-entry renderers. The renderer controls the
8
+ * inner content of an audit timeline entry; the surrounding framing (date column,
9
+ * timeline line, version badge, creator) stays with the host component.
10
+ */
11
+ declare abstract class AbstractAuditRendererComponent {
12
+ auditEntry: i0.InputSignal<AuditEntry>;
13
+ static ɵfac: i0.ɵɵFactoryDeclaration<AbstractAuditRendererComponent, never>;
14
+ static ɵcmp: i0.ɵɵComponentDeclaration<AbstractAuditRendererComponent, "yuv-abstract-audit-renderer", never, { "auditEntry": { "alias": "auditEntry"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
15
+ }
16
+
17
+ interface ResolvedAuditLabel {
18
+ label: string;
19
+ more?: string;
20
+ }
21
+ declare class DefaultAuditRendererComponent extends AbstractAuditRendererComponent {
22
+ #private;
23
+ protected resolved: i0.Signal<ResolvedAuditLabel>;
24
+ static ɵfac: i0.ɵɵFactoryDeclaration<DefaultAuditRendererComponent, never>;
25
+ static ɵcmp: i0.ɵɵComponentDeclaration<DefaultAuditRendererComponent, "yuv-default-audit-renderer", never, {}, {}, never, never, true, never>;
26
+ }
27
+
28
+ /**
29
+ * Attribute directive that renders an `AuditEntry` using the component registered with
30
+ * `AuditRendererService` for the entry's `action` (and optionally `subaction`). Falls
31
+ * back to `DefaultAuditRendererComponent` when no override is registered.
32
+ */
33
+ declare class AuditRendererDirective {
34
+ #private;
35
+ component: ComponentRef<AbstractAuditRendererComponent>;
36
+ yuvAuditRenderer: i0.InputSignal<AuditEntry>;
37
+ static ɵfac: i0.ɵɵFactoryDeclaration<AuditRendererDirective, never>;
38
+ static ɵdir: i0.ɵɵDirectiveDeclaration<AuditRendererDirective, "[yuvAuditRenderer]", never, { "yuvAuditRenderer": { "alias": "yuvAuditRenderer"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
39
+ }
40
+
41
+ /**
42
+ * Service for managing audit-entry renderers. Renderers are components that render the
43
+ * inner content of an audit entry in the timeline view of `ObjectAuditComponent`.
44
+ *
45
+ * Register a renderer for an `action` to override the default rendering of all audit
46
+ * entries with that action, or for an `action` + `subaction` pair to override a specific
47
+ * variant. Lookup prefers the subaction-specific renderer over the action-only renderer;
48
+ * if neither is registered, `DefaultAuditRendererComponent` is used.
49
+ */
50
+ declare class AuditRendererService {
51
+ #private;
52
+ /**
53
+ * Register a renderer for a specific audit `action`. Pass `subaction` to scope the
54
+ * renderer to a particular (action, subaction) pair.
55
+ */
56
+ registerAuditRenderer(cmp: Type<AbstractAuditRendererComponent>, action: number, subaction?: number): void;
57
+ /**
58
+ * Resolve the renderer for an audit entry. Tries `(action, subaction)` first, falls
59
+ * back to `(action)`, then to `DefaultAuditRendererComponent`.
60
+ */
61
+ getAuditRenderer(action: number, subaction?: number): Type<AbstractAuditRendererComponent>;
62
+ static ɵfac: i0.ɵɵFactoryDeclaration<AuditRendererService, never>;
63
+ static ɵprov: i0.ɵɵInjectableDeclaration<AuditRendererService>;
64
+ }
65
+
6
66
  /**
7
67
  * Abstract class to be extended by property renderers
8
68
  */
@@ -31,6 +91,12 @@ declare class DecimalRendererComponent extends AbstractRendererComponent<number>
31
91
  static ɵcmp: i0.ɵɵComponentDeclaration<DecimalRendererComponent, "yuv-decimal-renderer", never, {}, {}, never, never, true, never>;
32
92
  }
33
93
 
94
+ declare class FileSizeRendererComponent extends AbstractRendererComponent {
95
+ parsedValue: i0.Signal<number>;
96
+ static ɵfac: i0.ɵɵFactoryDeclaration<FileSizeRendererComponent, never>;
97
+ static ɵcmp: i0.ɵɵComponentDeclaration<FileSizeRendererComponent, "yuv-string-renderer", never, {}, {}, never, never, true, never>;
98
+ }
99
+
34
100
  declare class IconRendererComponent extends AbstractRendererComponent {
35
101
  #private;
36
102
  protected readonly customId: `${string}-${string}-${string}-${string}-${string}`;
@@ -49,7 +115,7 @@ declare class OrganizationRendererComponent extends AbstractRendererComponent {
49
115
  userAndRole: i0.WritableSignal<{
50
116
  type: string;
51
117
  label: string | undefined;
52
- }[] | undefined>;
118
+ }[]>;
53
119
  userAndRoleLoading: i0.Signal<boolean>;
54
120
  static ɵfac: i0.ɵɵFactoryDeclaration<OrganizationRendererComponent, never>;
55
121
  static ɵcmp: i0.ɵɵComponentDeclaration<OrganizationRendererComponent, "yuv-organization-renderer", never, {}, {}, never, never, true, never>;
@@ -70,7 +136,7 @@ declare class TableRendererComponent extends AbstractRendererComponent<any[]> {
70
136
  }[]>;
71
137
  protected tableData: i0.Signal<any[]>;
72
138
  protected reducedData: i0.Signal<boolean>;
73
- protected getCellValue(row: any[], key: string): any;
139
+ protected getCellValue(row: any[], key: string): string;
74
140
  static ɵfac: i0.ɵɵFactoryDeclaration<TableRendererComponent, never>;
75
141
  static ɵcmp: i0.ɵɵComponentDeclaration<TableRendererComponent, "yuv-table-renderer", never, {}, {}, never, never, true, never>;
76
142
  }
@@ -125,5 +191,5 @@ declare class RendererDirective {
125
191
  static ɵdir: i0.ɵɵDirectiveDeclaration<RendererDirective, "[yuvRenderer]", never, { "yuvRenderer": { "alias": "yuvRenderer"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
126
192
  }
127
193
 
128
- export { AbstractRendererComponent, DateTimeRendererComponent, DecimalRendererComponent, IconRendererComponent, IntegerRendererComponent, OrganizationRendererComponent, RendererDirective, RendererService, StringRendererComponent, UnknownRendererComponent };
194
+ export { AbstractAuditRendererComponent, AbstractRendererComponent, AuditRendererDirective, AuditRendererService, BooleanRendererComponent, DateTimeRendererComponent, DecimalRendererComponent, DefaultAuditRendererComponent, FileSizeRendererComponent, IconRendererComponent, IntegerRendererComponent, OrganizationRendererComponent, RendererDirective, RendererService, StringRendererComponent, TableRendererComponent, UnknownRendererComponent };
129
195
  export type { RendererComponent, RendererDirectiveInput };
@@ -109,6 +109,22 @@ declare const defaultHaloFocusOffset = 3;
109
109
  */
110
110
  declare const haloFocusStyles: Partial<CSSStyleDeclaration>;
111
111
 
112
+ /**
113
+ * Default interval (in milliseconds) for periodic PWA update checks.
114
+ *
115
+ * After the application stabilizes, {@link PwaUpdateService} polls the service
116
+ * worker for a newer deployed version on this interval. This complements the
117
+ * `VERSION_READY` event, which only fires while the tab is open and the service
118
+ * worker happens to detect a change — long-running sessions would otherwise
119
+ * never learn about a new release.
120
+ *
121
+ * Override via `providePwaUpdate({ checkInterval })`. Set `checkInterval` to `0`
122
+ * to disable polling and rely solely on `VERSION_READY`.
123
+ *
124
+ * @default 6 hours (21600000 milliseconds)
125
+ */
126
+ declare const pwaUpdateDefaultCheckInterval: number;
127
+
112
128
  /**
113
129
  * Default session duration (in milliseconds) when no explicit duration is provided.
114
130
  *
@@ -303,6 +319,35 @@ interface HaloFocusConfig {
303
319
  outerColor?: string;
304
320
  }
305
321
 
322
+ /**
323
+ * Configuration for the PWA update feature wired up by `providePwaUpdate()`.
324
+ *
325
+ * All label/message fields are passed through `TranslateService.instant()`, so
326
+ * you may supply either an i18n key or a literal string. When omitted, the
327
+ * built-in `yuv.pwa.update.*` keys are used.
328
+ */
329
+ interface PwaUpdateConfig {
330
+ /**
331
+ * Interval (in milliseconds) for periodic update checks after the app becomes
332
+ * stable. Defaults to {@link pwaUpdateDefaultCheckInterval} (6 hours).
333
+ * Set to `0` to disable polling and rely solely on the `VERSION_READY` event.
334
+ */
335
+ checkInterval?: number;
336
+ /** Title shown in the update confirm dialog. @default 'yuv.pwa.update.title' */
337
+ title?: string;
338
+ /** Message shown when a new version is ready to activate. @default 'yuv.pwa.update.message' */
339
+ message?: string;
340
+ /**
341
+ * Message shown when the service worker enters an unrecoverable state and the
342
+ * page must be reloaded. @default 'yuv.pwa.update.unrecoverable.message'
343
+ */
344
+ unrecoverableMessage?: string;
345
+ /** Confirm button label. @default 'yuv.pwa.update.confirm' */
346
+ confirmLabel?: string;
347
+ /** Cancel button label. @default 'yuv.pwa.update.cancel' */
348
+ cancelLabel?: string;
349
+ }
350
+
306
351
  type ChannelPayload = {
307
352
  type: ChannelMessage;
308
353
  expiresAt?: number;
@@ -389,6 +434,42 @@ interface SnackBarData {
389
434
  */
390
435
  declare function provideHaloFocus(config?: HaloFocusConfig): EnvironmentProviders;
391
436
 
437
+ /**
438
+ * Provides and initializes PWA update detection for the application.
439
+ *
440
+ * On startup it boots {@link PwaUpdateService}, which watches for newly deployed
441
+ * versions of the app and, instead of reloading silently, asks the user to
442
+ * confirm before activating the update and reloading the page.
443
+ *
444
+ * **Detection:** reacts to the service worker `VERSION_READY` event and also
445
+ * polls `checkForUpdate()` on an interval (default 6 hours) once the app is
446
+ * stable, so long-running sessions still pick up new releases.
447
+ *
448
+ * **Prerequisite:** the service worker must be registered separately via
449
+ * `provideServiceWorker('ngsw-worker.js', { enabled: !isDevMode() })`. When the
450
+ * service worker is disabled (e.g. dev mode) this provider is a harmless no-op.
451
+ *
452
+ * @param config - Optional configuration (check interval, custom dialog labels/messages).
453
+ * @returns EnvironmentProviders for the PWA update feature.
454
+ *
455
+ * @example
456
+ * // app.config.ts
457
+ * export const appConfig: ApplicationConfig = {
458
+ * providers: [
459
+ * provideServiceWorker('ngsw-worker.js', { enabled: !isDevMode() }),
460
+ * providePwaUpdate()
461
+ * ]
462
+ * };
463
+ *
464
+ * @example
465
+ * // Custom check interval (1 hour) and labels
466
+ * providePwaUpdate({
467
+ * checkInterval: 60 * 60 * 1000,
468
+ * message: 'A new version is available. Reload now?'
469
+ * });
470
+ */
471
+ declare function providePwaUpdate(config?: PwaUpdateConfig): EnvironmentProviders;
472
+
392
473
  /**
393
474
  * Provides and initializes the SessionService at application startup.
394
475
  *
@@ -770,6 +851,44 @@ declare class HaloUtilityService {
770
851
  static ɵprov: i0.ɵɵInjectableDeclaration<HaloUtilityService>;
771
852
  }
772
853
 
854
+ /**
855
+ * Detects newly deployed versions of the application (PWA) and lets the user
856
+ * decide when to apply them.
857
+ *
858
+ * **Flow (per the Angular `SwUpdate` documentation):**
859
+ * 1. **Check** — listens for the `VERSION_READY` event and, additionally, polls
860
+ * `checkForUpdate()` on an interval once the app is stable. The update is
861
+ * *not* applied automatically.
862
+ * 2. **Ask** — when a new version is ready, prompts the user via the framework
863
+ * {@link ConfirmService} dialog instead of reloading silently.
864
+ * 3. **Apply** — only after the user confirms, calls `activateUpdate()` and then
865
+ * reloads the page so the new version takes effect.
866
+ *
867
+ * The service is a no-op when the service worker is disabled (e.g. dev mode),
868
+ * so it is safe to provide unconditionally.
869
+ *
870
+ * Wire it up via `providePwaUpdate()` in `app.config.ts`. The service worker
871
+ * itself must still be registered separately via `provideServiceWorker(...)`.
872
+ *
873
+ * @see https://angular.dev/ecosystem/service-workers/communications
874
+ */
875
+ declare class PwaUpdateService {
876
+ #private;
877
+ /**
878
+ * Starts listening for updates. Called automatically by `providePwaUpdate()`.
879
+ * Does nothing when the service worker is not enabled.
880
+ */
881
+ init(config?: PwaUpdateConfig): void;
882
+ /**
883
+ * Manually triggers an update check (e.g. from a "check for updates" button).
884
+ * Resolves to `true` when a new version was found. Network errors are swallowed
885
+ * and resolve to `false`.
886
+ */
887
+ checkForUpdate(): Promise<boolean>;
888
+ static ɵfac: i0.ɵɵFactoryDeclaration<PwaUpdateService, never>;
889
+ static ɵprov: i0.ɵɵInjectableDeclaration<PwaUpdateService>;
890
+ }
891
+
773
892
  /**
774
893
  * Manages client-side session expiry: persists expiration, tracks user and HTTP activity,
775
894
  * shows a pre-expiry popup with an extend CTA, and syncs state across tabs via BroadcastChannel.
@@ -924,5 +1043,5 @@ declare class YuuvisClientFrameworkModule {
924
1043
  static ɵinj: i0.ɵɵInjectorDeclaration<YuuvisClientFrameworkModule>;
925
1044
  }
926
1045
 
927
- export { ChannelMessage, HaloFocusService, HaloUtilityService, SessionService, SnackBarComponent, SnackBarService, YuuvisClientFrameworkModule, defaultHaloFocusOffset, haloExcludedElementsInMatFormField, haloFocusNavigationKeys, haloFocusStyles, provideHaloFocus, provideSession, sessionActivityWindowBeforeEnd, sessionDefaultDuration, sessionPopupBeforeEnd };
928
- export type { ChannelPayload, HaloFocusConfig, SnackBarData, SnackBarLevel, SnackBarMessage, SnackBarOptions };
1046
+ export { ChannelMessage, HaloFocusService, HaloUtilityService, PwaUpdateService, SessionService, SnackBarComponent, SnackBarService, YuuvisClientFrameworkModule, defaultHaloFocusOffset, haloExcludedElementsInMatFormField, haloFocusNavigationKeys, haloFocusStyles, provideHaloFocus, providePwaUpdate, provideSession, pwaUpdateDefaultCheckInterval, sessionActivityWindowBeforeEnd, sessionDefaultDuration, sessionPopupBeforeEnd };
1047
+ export type { ChannelPayload, HaloFocusConfig, PwaUpdateConfig, SnackBarData, SnackBarLevel, SnackBarMessage, SnackBarOptions };