@pie-players/pie-assessment-player 0.3.70 → 0.3.72

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.
package/README.md CHANGED
@@ -33,14 +33,32 @@ const controller =
33
33
  host?.getAssessmentController?.();
34
34
  ```
35
35
 
36
- `waitForAssessmentController(timeoutMs)` resolves once the controller has
37
- been wired (the same signal the
38
- `AssessmentPlayerHooks.onAssessmentControllerReady(controller)` hook fires
39
- on). Use `getAssessmentController()` if you've already passed the readiness
40
- anchor synchronously.
36
+ `waitForAssessmentController(timeoutMs)` resolves after initialization and
37
+ hydration succeed. At that point the getter, ready hook, and ready event all
38
+ observe the same controller. The getter returns `null` while initialization is
39
+ pending or failed, and after disconnect. A waiter resolves to `null` when its
40
+ active attempt fails or is retired, or its own timeout expires. A waiter's
41
+ timeout does not cancel a still-active initialization.
42
+
43
+ Connect the element and assign `assessmentId`, `attemptId`, `assessment`, and
44
+ `hooks` in the same turn, or assign them before connecting. These assignments
45
+ are batched; changing assessment, attempt, or hooks later retires the previous
46
+ controller and starts a new one. Replace object properties to update them;
47
+ mutating a previously assigned object in place is not an update signal. Runtime
48
+ props, locale, and navigation visibility do not reload the assessment.
49
+
50
+ Failed initialization exposes an error and a keyboard-accessible Retry action.
51
+ The element invokes the ready hook and emits the ready event once per successful
52
+ initialization. Ready hooks are notifications: their rejection reaches `onError`
53
+ and `assessment-error` without undoing successful hydration.
41
54
 
42
55
  ### Lifecycle
43
56
 
57
+ The element owns its assessment controller and calls its idempotent `dispose()`
58
+ when inputs replace the attempt or the element disconnects. Disposal removes
59
+ listeners and makes the old handle unusable. The nested toolkit disposes any
60
+ coordinator it created; a coordinator supplied by the host remains host-owned.
61
+
44
62
  ```ts
45
63
  // hydrate runs as part of initialize(); call it again only on a manual reload.
46
64
  const unsubscribe = controller.subscribe(handleEvent);
@@ -61,6 +79,15 @@ shape the persistence strategy load/save methods exchange and the same
61
79
  shape per-section bridges roll up into via `updateSectionSession(sectionId,
62
80
  snapshot)`.
63
81
 
82
+ The default element captures the outgoing section before navigation replaces it,
83
+ including navigation through the controller. A returning section waits for its
84
+ canonical `engine-ready` stage and restores its saved section snapshot before
85
+ accepting input or session updates. The section region stays `aria-busy` during
86
+ this handoff. Failed restoration leaves the saved snapshot intact, reports
87
+ `assessment-error` and `onError` with phase `navigation`, and offers Retry.
88
+ Navigating away or disconnecting retires that handoff. This in-memory restoration
89
+ does not acknowledge durable saves; persistence still uses the host's strategy.
90
+
64
91
  ### Event stream
65
92
 
66
93
  The controller's typed event stream
@@ -4,25 +4,27 @@ import { AssessmentPlayerProgressSnapshot, AssessmentPlayerRuntimeHostContract,
4
4
  import { AssessmentDefinition, AssessmentPlayerHooks, AssessmentPlayerRuntimeConfig } from '../types.js';
5
5
  export declare class AssessmentPlayerDefaultElement extends HTMLElement implements AssessmentPlayerRuntimeHostContract {
6
6
  static get observedAttributes(): string[];
7
- assessmentId: string;
8
- attemptId: string;
7
+ private _assessmentId;
8
+ private _attemptId;
9
9
  /**
10
10
  * Interface locale for this player's own navigation chrome, as a BCP-47 tag.
11
11
  * Empty means the graceful default, `en-US`. Also forwarded to the section
12
12
  * element, which passes it on to the toolkit.
13
13
  */
14
- locale: string;
15
- assessment: AssessmentDefinition | null;
16
- env: Env | null;
17
- coordinator: unknown;
18
- hooks: AssessmentPlayerHooks | null;
19
- showNavigation: boolean | string | null | undefined;
20
- sectionPlayerLayout: "splitpane" | "vertical";
21
- playerType: "iife" | "esm" | "preloaded";
14
+ private _locale;
15
+ private _assessment;
16
+ private _env;
17
+ private _coordinator;
18
+ private _hooks;
19
+ private _showNavigation;
20
+ private _sectionPlayerLayout;
21
+ private _playerType;
22
22
  private _debug;
23
23
  private controller;
24
- private controllerReadyPromise;
25
- private controllerReadyResolve;
24
+ private initializingController;
25
+ private generation;
26
+ private reconcileQueued;
27
+ private controllerWaiters;
26
28
  /**
27
29
  * This player's own provider rather than a context read. Its navigation sits
28
30
  * beside the section host, not inside it, so there is no published toolkit
@@ -33,6 +35,8 @@ export declare class AssessmentPlayerDefaultElement extends HTMLElement implemen
33
35
  private unsubscribeI18n?;
34
36
  private sectionHost;
35
37
  private sectionControllerRef;
38
+ private sectionSessionReady;
39
+ private detachSectionListeners?;
36
40
  /**
37
41
  * Rendered content, tracked so a re-render can replace it without clearing the
38
42
  * announcer below. A live region only announces changes that happen while it is
@@ -51,8 +55,27 @@ export declare class AssessmentPlayerDefaultElement extends HTMLElement implemen
51
55
  private detachInstrumentationBridge?;
52
56
  private _sectionPlayerRuntime;
53
57
  private readiness;
54
- constructor();
55
- attributeChangedCallback(name: string, _oldValue: string | null, value: string | null): void;
58
+ get assessmentId(): string;
59
+ set assessmentId(value: string);
60
+ get attemptId(): string;
61
+ set attemptId(value: string);
62
+ get assessment(): AssessmentDefinition | null;
63
+ set assessment(value: AssessmentDefinition | null);
64
+ get hooks(): AssessmentPlayerHooks | null;
65
+ set hooks(value: AssessmentPlayerHooks | null);
66
+ get locale(): string;
67
+ set locale(value: string);
68
+ get env(): Env | null;
69
+ set env(value: Env | null);
70
+ get coordinator(): unknown;
71
+ set coordinator(value: unknown);
72
+ get showNavigation(): boolean | string | null | undefined;
73
+ set showNavigation(value: boolean | string | null | undefined);
74
+ get sectionPlayerLayout(): "splitpane" | "vertical";
75
+ set sectionPlayerLayout(value: "splitpane" | "vertical");
76
+ get playerType(): "iife" | "esm" | "preloaded";
77
+ set playerType(value: "iife" | "esm" | "preloaded");
78
+ attributeChangedCallback(name: string, oldValue: string | null, value: string | null): void;
56
79
  /**
57
80
  * Catalog loading is async, so the first paint carries whatever the provider
58
81
  * already holds and the subscription repaints once the requested locale
@@ -70,11 +93,15 @@ export declare class AssessmentPlayerDefaultElement extends HTMLElement implemen
70
93
  set sectionPlayerRuntime(value: AssessmentPlayerRuntimeConfig["sectionPlayerRuntime"]);
71
94
  connectedCallback(): void;
72
95
  disconnectedCallback(): void;
96
+ private settleControllerWaiters;
97
+ private retireController;
98
+ private queueReconciliation;
73
99
  private dispatch;
74
100
  private bootstrapController;
75
101
  private resolveInstrumentationProvider;
76
102
  private attachInstrumentationBridge;
77
103
  private renderEmptyState;
104
+ private createStatusContent;
78
105
  /**
79
106
  * Replace the rendered content, leaving the announcer in place.
80
107
  */
@@ -108,6 +135,8 @@ export declare class AssessmentPlayerDefaultElement extends HTMLElement implemen
108
135
  private syncCurrentSectionSessionIntoAssessment;
109
136
  private attachSectionControllerReadyListener;
110
137
  private render;
138
+ /** Locale and chrome changes must not remount a section or reload its attempt. */
139
+ private updateNavigationChrome;
111
140
  /**
112
141
  * Announce the new position, once the live region has been in the document long
113
142
  * enough for the change to register as a change.
@@ -62,13 +62,15 @@ export type AssessmentControllerEvent = {
62
62
  export interface AssessmentControllerHandle {
63
63
  /**
64
64
  * Bootstrap the controller: build the delivery plan, ensure a
65
- * session exists, run `hydrate()`, and fire
66
- * `onAssessmentControllerReady`.
65
+ * session exists, and run `hydrate()` once. The owning element
66
+ * publishes readiness and invokes `onAssessmentControllerReady`.
67
67
  *
68
68
  * Called by the assessment player when the player CE mounts. Hosts
69
69
  * normally do not call this directly.
70
70
  */
71
71
  initialize(): Promise<void>;
72
+ /** Retire this attempt, detach listeners, and notify its disposal hook once. */
73
+ dispose(): Promise<void>;
72
74
  /**
73
75
  * Load and apply a previously persisted assessment session via the
74
76
  * registered `AssessmentSessionPersistenceStrategy`. Falls back to
@@ -173,6 +175,9 @@ export declare class AssessmentController implements AssessmentControllerHandle
173
175
  private readiness;
174
176
  private submitted;
175
177
  private persistenceStrategy?;
178
+ private initializationPromise?;
179
+ private disposalPromise?;
180
+ private disposed;
176
181
  private readonly storageContext;
177
182
  constructor(args: {
178
183
  assessmentId: string;
@@ -181,12 +186,15 @@ export declare class AssessmentController implements AssessmentControllerHandle
181
186
  hooks?: AssessmentPlayerHooks;
182
187
  });
183
188
  private emit;
189
+ private assertActive;
184
190
  private handleError;
185
191
  private getPersistenceStrategy;
186
192
  private createDeliveryPlan;
187
193
  private ensureSession;
188
194
  initialize(): Promise<void>;
195
+ private initializeOnce;
189
196
  hydrate(): Promise<void>;
197
+ dispose(): Promise<void>;
190
198
  persist(): Promise<void>;
191
199
  getSession(): AssessmentSession | null;
192
200
  getRuntimeState(): AssessmentControllerRuntimeState;
@@ -60,7 +60,9 @@ var e = {
60
60
  resizePassageAndMediaA11y: "Panelen met tekst en media verkleinen of vergroten",
61
61
  assessment: {
62
62
  sectionPosition: "Onderdeel {position} van {total}",
63
- noSections: "Geen onderdelen"
63
+ noSections: "Geen onderdelen",
64
+ loadFailed: "De toets kon niet worden geladen. Probeer het opnieuw.",
65
+ restoreFailed: "Je opgeslagen antwoorden voor dit onderdeel konden niet worden hersteld. Probeer het opnieuw."
64
66
  },
65
67
  formative: {
66
68
  checkAnswer: "Antwoord controleren",