@pie-players/pie-assessment-player 0.3.74 → 0.3.75

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
@@ -43,10 +43,10 @@ pending or failed, and after disconnect. A waiter resolves to `null` when its
43
43
  active attempt fails or is retired, or its own timeout expires. A waiter's
44
44
  timeout does not cancel a still-active initialization.
45
45
 
46
- Connect the element and assign `assessmentId`, `attemptId`, `assessment`, and
47
- `hooks` in the same turn, or assign them before connecting. These assignments
48
- are batched; changing assessment, attempt, or hooks later retires the previous
49
- controller and starts a new one. Replace object properties to update them;
46
+ Connect the element and assign `assessmentId`, `attemptId`, `assessment`,
47
+ `hooks` and `session` in the same turn, or assign them before connecting. These
48
+ assignments are batched; changing assessment, attempt, hooks or session later
49
+ retires the previous controller and starts a new one. Replace object properties to update them;
50
50
  mutating a previously assigned object in place is not an update signal. Runtime
51
51
  props, locale, and navigation visibility do not reload the assessment.
52
52
 
@@ -71,7 +71,8 @@ controller.navigateTo("section-2");
71
71
  controller.navigateNext();
72
72
  controller.navigatePrevious();
73
73
 
74
- // persist on whatever cadence the host wants; submit() always persists.
74
+ // persist on whatever cadence the host wants; saves run in call order.
75
+ // submit() saves first and rejects, unsubmitted, if that save fails.
75
76
  await controller.persist();
76
77
  await controller.submit();
77
78
  unsubscribe();
@@ -82,10 +83,20 @@ shape the persistence strategy load/save methods exchange and the same
82
83
  shape per-section bridges roll up into via `updateSectionSession(sectionId,
83
84
  snapshot)`.
84
85
 
86
+ The element's `session` property takes that shape too. The controller's first
87
+ hydrate applies an assigned session in place of the strategy's `loadSession`;
88
+ the strategy still receives every `persist()`. The value is consumed once: a
89
+ rebuild for another reason, such as an `assessment` change, hydrates from the
90
+ strategy. Assigning a session equal to the controller's, as writing
91
+ `assessment-session-changed` back into the property does, is a no-op, and so is
92
+ `null` once the controller is ready. Reading `session` returns the assigned value
93
+ until the controller is ready and a copy of the controller's session after.
94
+
85
95
  The default element captures the outgoing section before navigation replaces it,
86
- including navigation through the controller. A returning section waits for its
87
- canonical `engine-ready` stage and restores its saved section snapshot before
88
- accepting input or session updates. The section region stays `aria-busy` during
96
+ including navigation through the controller. A returning section receives its
97
+ saved section snapshot as the section player's `session` property, which its
98
+ controller applies before the canonical `engine-ready` stage; the section accepts
99
+ input and session updates from `engine-ready` on. The section region stays `aria-busy` during
89
100
  this handoff. Failed restoration leaves the saved snapshot intact, reports
90
101
  `assessment-error` and `onError` with phase `navigation`, and offers Retry.
91
102
  Navigating away or disconnecting retires that handoff. This in-memory restoration
@@ -101,7 +112,8 @@ change only — section-level events flow through the embedded
101
112
 
102
113
  - `assessment-route-changed` — section-level navigation moved (index / id,
103
114
  `previousSectionId`, `canNext` / `canPrevious`).
104
- - `assessment-session-applied` — `hydrate()` loaded a persisted session.
115
+ - `assessment-session-applied` — `hydrate()` applied a session, persisted or
116
+ assigned as `session`.
105
117
  - `assessment-session-changed` — assessment session mutated (navigation,
106
118
  per-section snapshot upsert). The active section's snapshot is upserted once
107
119
  for each `session-changed` it emits.
@@ -207,15 +219,18 @@ import * as delivery from "@pie-element/multiple-choice/browser/delivery";
207
219
  import * as controller from "@pie-element/multiple-choice/browser/controller";
208
220
  import manifest from "../package.json"; // pins "@pie-element/multiple-choice" exactly
209
221
 
210
- registerPreloadedElements([
211
- {
212
- tag: "pie-element-multiple-choice",
213
- package: "@pie-element/multiple-choice",
214
- version: manifest.dependencies["@pie-element/multiple-choice"],
215
- element: delivery,
216
- controller,
217
- },
218
- ]);
222
+ registerPreloadedElements(
223
+ [
224
+ {
225
+ tag: "pie-element-multiple-choice",
226
+ package: "@pie-element/multiple-choice",
227
+ version: manifest.dependencies["@pie-element/multiple-choice"],
228
+ element: delivery,
229
+ controller,
230
+ },
231
+ ],
232
+ { math: { assetRoot: "https://assets.example.com/npm" } },
233
+ );
219
234
  ```
220
235
 
221
236
  - A tag missing at pre-warm leaves the section's items unmounted and raises a
@@ -231,6 +246,10 @@ registerPreloadedElements([
231
246
  an element can typeset with a MathJax it was not built for. Elements that
232
247
  bundle their own MathJax share none
233
248
  ([One MathJax version per page](../../docs/item-player/loading-strategies.md#one-mathjax-version-per-page)).
249
+ - Pass `math.assetRoot`, an npm root serving the fonts and speech the elements'
250
+ bundled MathJax loads, or `math.assetUrls`, each file's URL; without either,
251
+ elements on adapter 0.1.3 or later render without web fonts and speech
252
+ ([MathJax assets](../../docs/item-player/loading-strategies.md#mathjax-assets)).
234
253
  - Register one version per package; registering a second version throws.
235
254
  - Register each package's `controller` unless the item players are hosted
236
255
  (`sectionPlayerRuntime.player.hosted`, or an enabled
@@ -1,7 +1,7 @@
1
1
  import { Env } from '@pie-players/pie-players-shared/types';
2
2
  import { AssessmentControllerHandle } from '../controller/AssessmentController.js';
3
3
  import { AssessmentPlayerProgressSnapshot, AssessmentPlayerRuntimeHostContract, AssessmentPlayerSnapshot } from '../contracts/runtime-host-contract.js';
4
- import { AssessmentDefinition, AssessmentPlayerHooks, AssessmentPlayerRuntimeConfig } from '../types.js';
4
+ import { AssessmentDefinition, AssessmentPlayerHooks, AssessmentPlayerRuntimeConfig, AssessmentSession } from '../types.js';
5
5
  export declare class AssessmentPlayerDefaultElement extends HTMLElement implements AssessmentPlayerRuntimeHostContract {
6
6
  static get observedAttributes(): string[];
7
7
  private _assessmentId;
@@ -16,6 +16,13 @@ export declare class AssessmentPlayerDefaultElement extends HTMLElement implemen
16
16
  private _env;
17
17
  private _coordinator;
18
18
  private _hooks;
19
+ /**
20
+ * The assigned session until a controller initializes from it. A later
21
+ * rebuild, such as one an `assessment` change causes, hydrates from the
22
+ * persistence strategy rather than reapplying a value the learner has since
23
+ * moved past.
24
+ */
25
+ private pendingSession;
19
26
  private _showNavigation;
20
27
  private _sectionPlayerLayout;
21
28
  private _playerType;
@@ -63,6 +70,12 @@ export declare class AssessmentPlayerDefaultElement extends HTMLElement implemen
63
70
  set assessment(value: AssessmentDefinition | null);
64
71
  get hooks(): AssessmentPlayerHooks | null;
65
72
  set hooks(value: AssessmentPlayerHooks | null);
73
+ /**
74
+ * Before a controller is ready, the assigned session; after, a copy of the
75
+ * controller's.
76
+ */
77
+ get session(): AssessmentSession | null;
78
+ set session(value: AssessmentSession | null);
66
79
  get locale(): string;
67
80
  set locale(value: string);
68
81
  get env(): Env | null;
@@ -133,6 +146,10 @@ export declare class AssessmentPlayerDefaultElement extends HTMLElement implemen
133
146
  private updateCurrentSectionRuntime;
134
147
  private cleanupTtsForSectionNavigation;
135
148
  private syncCurrentSectionSessionIntoAssessment;
149
+ /**
150
+ * `restoring` is whether the section was handed a saved session, which it
151
+ * applies while it creates its controller, before `engine-ready`.
152
+ */
136
153
  private attachSectionControllerReadyListener;
137
154
  private render;
138
155
  /** Locale and chrome changes must not remount a section or reload its attempt. */
@@ -3,6 +3,7 @@ import { AssessmentPlayerRuntimeConfig } from '../types.js';
3
3
  export type ResolveAssessmentSectionPlayerRuntimeArgs = {
4
4
  sectionPlayerRuntime?: AssessmentPlayerRuntimeConfig["sectionPlayerRuntime"];
5
5
  playerType: "iife" | "esm" | "preloaded";
6
+ assessmentId?: string;
6
7
  attemptId?: string;
7
8
  env?: Record<string, unknown> | null;
8
9
  coordinator?: unknown;
@@ -83,8 +83,8 @@ export interface AssessmentControllerHandle {
83
83
  * persistence strategy.
84
84
  *
85
85
  * Hosts call this on whatever cadence they choose (debounced on
86
- * change, on visibility change, on submit). `submit()` always
87
- * `persist()`s as its final step.
86
+ * change, on visibility change, on submit). Saves run in call order.
87
+ * A failed save goes to `onError` and the returned promise resolves.
88
88
  */
89
89
  persist(): Promise<void>;
90
90
  /**
@@ -123,9 +123,9 @@ export interface AssessmentControllerHandle {
123
123
  */
124
124
  navigatePrevious(): boolean;
125
125
  /**
126
- * Mark the assessment as submitted, emit
127
- * `assessment-submission-state-changed`, and `persist()` the final
128
- * snapshot.
126
+ * Save the final snapshot, then mark the assessment as submitted and
127
+ * emit `assessment-submission-state-changed`. A failed save goes to
128
+ * `onError` and rejects, and the assessment stays unsubmitted.
129
129
  */
130
130
  submit(): Promise<void>;
131
131
  /**
@@ -174,16 +174,25 @@ export declare class AssessmentController implements AssessmentControllerHandle
174
174
  private session;
175
175
  private readiness;
176
176
  private submitted;
177
+ private saveQueue;
177
178
  private persistenceStrategy?;
178
179
  private initializationPromise?;
179
180
  private disposalPromise?;
180
181
  private disposed;
181
182
  private readonly storageContext;
183
+ /** Taken by the first hydrate; a later `hydrate()` loads from the strategy. */
184
+ private initialSession;
182
185
  constructor(args: {
183
186
  assessmentId: string;
184
187
  attemptId?: string;
185
188
  assessment: AssessmentDefinition | null;
186
189
  hooks?: AssessmentPlayerHooks;
190
+ /**
191
+ * A host-supplied session, applied by the first hydrate in place of the
192
+ * persistence strategy's `loadSession` result. The controller keeps a
193
+ * copy, so later updates never write into the host's object.
194
+ */
195
+ initialSession?: AssessmentSession | null;
187
196
  });
188
197
  private emit;
189
198
  private assertActive;
@@ -196,6 +205,11 @@ export declare class AssessmentController implements AssessmentControllerHandle
196
205
  hydrate(): Promise<void>;
197
206
  dispose(): Promise<void>;
198
207
  persist(): Promise<void>;
208
+ /**
209
+ * Saves run one at a time, in call order, so a slow older write can never
210
+ * land after a newer one. A failed save rejects only its own caller.
211
+ */
212
+ private save;
199
213
  getSession(): AssessmentSession | null;
200
214
  getRuntimeState(): AssessmentControllerRuntimeState;
201
215
  private buildNavigationSnapshot;
@@ -306,30 +306,6 @@ var e = {
306
306
  textToSpeech: {
307
307
  name: "Voorlezen",
308
308
  description: "Inhoud laten voorlezen",
309
- toolA11y: "Voorleeshulp",
310
- title: "Voorlezen",
311
- initializing: "Wordt gestart…",
312
- initFailed: "Voorlezen kon niet worden gestart.",
313
- selectText: "Selecteer tekst op de pagina om die te laten voorlezen.",
314
- speed: "Snelheid:",
315
- play: "Voorlezen",
316
- pause: "Pauzeren",
317
- resume: "Hervatten",
318
- stop: "Stoppen",
319
- speaking: "Aan het voorlezen…",
320
- paused: "Gepauzeerd",
321
- charactersSelected: {
322
- one: "{count} teken geselecteerd",
323
- other: "{count} tekens geselecteerd"
324
- },
325
- rate: {
326
- slow: "Langzaam",
327
- slower: "Langzamer",
328
- normal: "Normaal",
329
- faster: "Sneller",
330
- fast: "Snel",
331
- veryFast: "Zeer snel"
332
- },
333
309
  inline: {
334
310
  controlsA11y: "Voorleesknoppen",
335
311
  playbackSpeedA11y: "Voorleessnelheid",
@@ -435,6 +411,7 @@ var e = {
435
411
  resolvedTools: "Toegekende hulpmiddelen (toolkit)",
436
412
  provenanceSummary: "Overzicht van herkomst van het hulpmiddelenbeleid",
437
413
  perToolDecisions: "Beslissingen per hulpmiddel",
414
+ policyDiagnostics: "Diagnose van het hulpmiddelenbeleid",
438
415
  profileReadOnly: "PNP-profiel (alleen lezen)"
439
416
  },
440
417
  tts: {
@@ -1,46 +1,44 @@
1
- import "@pie-players/pie-section-player/components/section-player-splitpane-element";
2
- import "@pie-players/pie-section-player/components/section-player-vertical-element";
3
- import { SECTION_PLAYER_PUBLIC_EVENTS as N } from "@pie-players/pie-section-player/contracts/public-events";
4
- import { coerceBooleanLike as R, defineCustomElementSafely as B } from "@pie-players/pie-players-shared";
5
- import { createNewAssessmentSession as V, frameworkErrorFromUnknown as Q, setCurrentSectionPosition as J, upsertSectionSession as K } from "@pie-players/pie-assessment-toolkit";
6
- var b = (e) => {
1
+ import "@pie-players/pie-section-player";
2
+ import { coerceBooleanLike as R, defineCustomElementSafely as O } from "@pie-players/pie-players-shared";
3
+ import { createNewAssessmentSession as Q, frameworkErrorFromUnknown as J, setCurrentSectionPosition as K, upsertSectionSession as Y } from "@pie-players/pie-assessment-toolkit";
4
+ var y = (e) => {
7
5
  if (e === null || typeof e != "object") return e;
8
- if (Array.isArray(e)) return e.map(b);
6
+ if (Array.isArray(e)) return e.map(y);
9
7
  const t = {};
10
- for (const i in e) Object.hasOwn(e, i) && (t[i] = b(e[i]));
8
+ for (const i in e) Object.hasOwn(e, i) && (t[i] = y(e[i]));
11
9
  return t;
12
10
  };
13
- function S(e) {
11
+ function w(e) {
14
12
  "@babel/helpers - typeof";
15
- return S = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(t) {
13
+ return w = typeof Symbol == "function" && typeof Symbol.iterator == "symbol" ? function(t) {
16
14
  return typeof t;
17
15
  } : function(t) {
18
16
  return t && typeof Symbol == "function" && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t;
19
- }, S(e);
17
+ }, w(e);
20
18
  }
21
- function Y(e, t) {
22
- if (S(e) != "object" || !e) return e;
19
+ function Z(e, t) {
20
+ if (w(e) != "object" || !e) return e;
23
21
  var i = e[Symbol.toPrimitive];
24
22
  if (i !== void 0) {
25
23
  var s = i.call(e, t || "default");
26
- if (S(s) != "object") return s;
24
+ if (w(s) != "object") return s;
27
25
  throw new TypeError("@@toPrimitive must return a primitive value.");
28
26
  }
29
27
  return (t === "string" ? String : Number)(e);
30
28
  }
31
- function Z(e) {
32
- var t = Y(e, "string");
33
- return S(t) == "symbol" ? t : t + "";
29
+ function X(e) {
30
+ var t = Z(e, "string");
31
+ return w(t) == "symbol" ? t : t + "";
34
32
  }
35
- function g(e, t, i) {
36
- return (t = Z(t)) in e ? Object.defineProperty(e, t, {
33
+ function f(e, t, i) {
34
+ return (t = X(t)) in e ? Object.defineProperty(e, t, {
37
35
  value: i,
38
36
  enumerable: !0,
39
37
  configurable: !0,
40
38
  writable: !0
41
39
  }) : e[t] = i, e;
42
40
  }
43
- function O(e, t) {
41
+ function H(e, t) {
44
42
  const i = `[${e}]`;
45
43
  return {
46
44
  debug: (s, ...n) => {
@@ -51,22 +49,35 @@ function O(e, t) {
51
49
  error: (s, ...n) => console.error(i, s, ...n)
52
50
  };
53
51
  }
54
- function H() {
52
+ function q() {
55
53
  return typeof window < "u" && window.PIE_DEBUG === !0;
56
54
  }
57
- function q(e) {
55
+ function G(e) {
58
56
  if (!e || typeof e != "object") return !1;
59
57
  const t = e;
60
58
  return typeof t.providerId == "string" && typeof t.providerName == "string" && typeof t.initialize == "function" && typeof t.trackError == "function" && typeof t.trackEvent == "function" && typeof t.destroy == "function" && typeof t.isReady == "function";
61
59
  }
62
- var X = O("instrumentation-event-bridge", () => H());
63
- function ee(e) {
64
- return e && typeof e == "object" ? e : typeof e > "u" ? {} : { value: e };
65
- }
60
+ var ee = H("instrumentation-event-bridge", () => q());
66
61
  function te(e) {
67
- X.warn(`[InstrumentationEventBridge:${e}] Ignoring invalid instrumentation provider; expected InstrumentationProvider contract`);
62
+ if (e === null) return !0;
63
+ const t = typeof e;
64
+ if (t === "function" || t === "symbol") return !1;
65
+ if (t !== "object" || Array.isArray(e)) return !0;
66
+ const i = Object.getPrototypeOf(e);
67
+ return i === Object.prototype || i === null;
68
68
  }
69
69
  function ie(e) {
70
+ if (e && typeof e == "object") {
71
+ const t = {};
72
+ for (const [i, s] of Object.entries(e)) te(s) && (t[i] = s);
73
+ return t;
74
+ }
75
+ return typeof e > "u" ? {} : { value: e };
76
+ }
77
+ function se(e) {
78
+ ee.warn(`[InstrumentationEventBridge:${e}] Ignoring invalid instrumentation provider; expected InstrumentationProvider contract`);
79
+ }
80
+ function ne(e) {
70
81
  try {
71
82
  e.provider.trackError(e.error instanceof Error ? e.error : new Error(String(e.error ?? "Unknown instrumentation bridge error")), {
72
83
  component: e.component,
@@ -76,13 +87,13 @@ function ie(e) {
76
87
  } catch {
77
88
  }
78
89
  }
79
- function se(e) {
90
+ function oe(e) {
80
91
  if (!e.host || !e.eventMap.length) return () => {
81
92
  };
82
93
  if (!e.instrumentationProvider) return () => {
83
94
  };
84
- if (!q(e.instrumentationProvider))
85
- return e.debug && te(e.component), () => {
95
+ if (!G(e.instrumentationProvider))
96
+ return e.debug && se(e.component), () => {
86
97
  };
87
98
  const t = e.instrumentationProvider, i = [], s = Math.max(0, e.dedupeWindowMs ?? 0), n = /* @__PURE__ */ new Map(), o = (r, a) => {
88
99
  const h = [
@@ -99,7 +110,7 @@ function se(e) {
99
110
  const a = (h) => {
100
111
  try {
101
112
  if (!t.isReady() || e.shouldTrackEvent && !e.shouldTrackEvent(h)) return;
102
- const d = ee(h.detail), c = (/* @__PURE__ */ new Date()).toISOString(), m = {
113
+ const d = ie(h.detail), c = (/* @__PURE__ */ new Date()).toISOString(), p = {
103
114
  ...d,
104
115
  ...e.staticAttributes,
105
116
  component: e.component,
@@ -107,13 +118,13 @@ function se(e) {
107
118
  timestamp: c
108
119
  };
109
120
  if (s > 0) {
110
- const u = Date.now(), l = o(r, m), f = n.get(l);
111
- if (typeof f == "number" && u - f < s) return;
121
+ const u = Date.now(), l = o(r, p), m = n.get(l);
122
+ if (typeof m == "number" && u - m < s) return;
112
123
  n.set(l, u);
113
124
  }
114
- t.trackEvent(r.instrumentationEventName, { ...m });
125
+ t.trackEvent(r.instrumentationEventName, { ...p });
115
126
  } catch (d) {
116
- ie({
127
+ ne({
117
128
  provider: t,
118
129
  component: e.component,
119
130
  error: d,
@@ -133,15 +144,15 @@ function T(e) {
133
144
  const t = e;
134
145
  return typeof t?.noticeError == "function" && typeof t.addPageAction == "function";
135
146
  }
136
- function y() {
147
+ function b() {
137
148
  if (typeof window > "u") return;
138
149
  const { newrelic: e, NREUM: t } = window;
139
150
  if (T(e)) return e;
140
151
  if (T(t)) return t;
141
152
  }
142
- var ne = class {
153
+ var re = class {
143
154
  constructor() {
144
- g(this, "initialized", !1), g(this, "config", void 0);
155
+ f(this, "initialized", !1), f(this, "config", void 0);
145
156
  }
146
157
  trackError(e, t) {
147
158
  if (!this.guardedOperation("trackError", e, t) || !this.applyErrorFilter(e, t)) return;
@@ -203,31 +214,31 @@ var ne = class {
203
214
  transformAttributes(e) {
204
215
  return this.config?.attributeTransformer ? this.config.attributeTransformer(e) : e;
205
216
  }
206
- }, oe = class extends ne {
217
+ }, ae = class extends re {
207
218
  constructor(...e) {
208
- super(...e), g(this, "providerId", "newrelic"), g(this, "providerName", "New Relic");
219
+ super(...e), f(this, "providerId", "newrelic"), f(this, "providerName", "New Relic");
209
220
  }
210
221
  async initialize(e) {
211
- this.config = e, this.initialized = !0, this.config?.debug && console.log(y() ? "[NewRelicProvider] Initialized successfully" : "[NewRelicProvider] Initialized; sends once the New Relic agent is on the page");
222
+ this.config = e, this.initialized = !0, this.config?.debug && console.log(b() ? "[NewRelicProvider] Initialized successfully" : "[NewRelicProvider] Initialized; sends once the New Relic agent is on the page");
212
223
  }
213
224
  isReady() {
214
- return this.initialized && y() !== void 0;
225
+ return this.initialized && b() !== void 0;
215
226
  }
216
227
  destroy() {
217
228
  this.initialized = !1, this.config?.debug && console.log("[NewRelicProvider] Destroyed");
218
229
  }
219
230
  doTrackError(e, t) {
220
- y()?.noticeError(e, t);
231
+ b()?.noticeError(e, t);
221
232
  }
222
233
  doTrackEvent(e, t) {
223
- y()?.addPageAction(e, t);
234
+ b()?.addPageAction(e, t);
224
235
  }
225
236
  doSetUserContext(e, t) {
226
- const i = y();
237
+ const i = b();
227
238
  if (typeof i?.setUserId == "function" && i.setUserId(e), t && typeof i?.setCustomAttribute == "function") for (const [s, n] of Object.entries(t)) i.setCustomAttribute(s, n);
228
239
  }
229
240
  doSetGlobalAttributes(e) {
230
- const t = y();
241
+ const t = b();
231
242
  if (typeof t?.setCustomAttribute == "function") for (const [i, s] of Object.entries(e)) t.setCustomAttribute(i, s);
232
243
  }
233
244
  };
@@ -251,25 +262,25 @@ function F(e) {
251
262
  const t = e.trackPageActions;
252
263
  return typeof t == "boolean" ? t : void 0;
253
264
  }
254
- function re(e, t) {
265
+ function le(e, t) {
255
266
  return e.kind === "value" || e.kind === "null" ? e : t;
256
267
  }
257
- var I, ae = O("instrumentation-provider-resolution", () => H());
258
- function le() {
259
- return I || (I = new oe(), I.initialize()), I;
268
+ var I, ce = H("instrumentation-provider-resolution", () => q());
269
+ function de() {
270
+ return I || (I = new ae(), I.initialize()), I;
260
271
  }
261
- function ce(e) {
262
- const t = D(e.runtimePlayer), i = D(e.player), s = F(t), n = F(i), o = s ?? n ?? !1, r = re(M(t), M(i));
272
+ function ue(e) {
273
+ const t = D(e.runtimePlayer), i = D(e.player), s = F(t), n = F(i), o = s ?? n ?? !1, r = le(M(t), M(i));
263
274
  if (r.kind !== "null") {
264
- if (r.kind === "unset") return o ? le() : void 0;
265
- if (q(r.value)) return r.value;
275
+ if (r.kind === "unset") return o ? de() : void 0;
276
+ if (G(r.value)) return r.value;
266
277
  if (e.debug) {
267
278
  const a = e.component ? `[${e.component}] ` : "";
268
- ae.warn(`${a}Ignoring invalid instrumentation provider; expected InstrumentationProvider contract`);
279
+ ce.warn(`${a}Ignoring invalid instrumentation provider; expected InstrumentationProvider contract`);
269
280
  }
270
281
  }
271
282
  }
272
- var de = [
283
+ var he = [
273
284
  {
274
285
  sourceEventName: "assessment-controller-ready",
275
286
  instrumentationEventName: "pie-assessment-controller-ready"
@@ -302,17 +313,17 @@ var de = [
302
313
  sourceEventName: "assessment-error",
303
314
  instrumentationEventName: "pie-assessment-error"
304
315
  }
305
- ], ue = { "nl-NL": () => import("./nl-NL-BVlv1TF9.js") }, he = ["en-US", "nl-NL"];
306
- async function pe(e) {
307
- const t = ue[e];
316
+ ], pe = { "nl-NL": () => import("./nl-NL-CfezB59b.js") }, me = ["en-US", "nl-NL"];
317
+ async function ge(e) {
318
+ const t = pe[e];
308
319
  if (!t) throw new Error(`No bundled i18n catalog for locale: ${e}`);
309
320
  return (await t()).default;
310
321
  }
311
- function w(e) {
322
+ function A(e) {
312
323
  return e ? e.trim().replace(/_/g, "-").split(/[.@]/)[0].toLowerCase() : "";
313
324
  }
314
- function me(e) {
315
- const t = w(e);
325
+ function fe(e) {
326
+ const t = A(e);
316
327
  if (!t) return [];
317
328
  const i = t.split("-"), s = [];
318
329
  for (let n = i.length; n > 0; n--) {
@@ -321,13 +332,13 @@ function me(e) {
321
332
  }
322
333
  return s;
323
334
  }
324
- function ge(e, t) {
325
- for (const i of me(e)) {
326
- const s = t.find((n) => w(n) === i);
335
+ function ye(e, t) {
336
+ for (const i of fe(e)) {
337
+ const s = t.find((n) => A(n) === i);
327
338
  if (s != null) return s;
328
339
  }
329
340
  }
330
- var fe = {
341
+ var ve = {
331
342
  common: {
332
343
  close: "Close",
333
344
  cancel: "Cancel",
@@ -635,30 +646,6 @@ var fe = {
635
646
  textToSpeech: {
636
647
  name: "Text to Speech",
637
648
  description: "Read content aloud",
638
- toolA11y: "Text-to-speech tool",
639
- title: "Text-to-Speech",
640
- initializing: "Initializing…",
641
- initFailed: "Failed to initialize text-to-speech.",
642
- selectText: "Select text on the page to read it aloud.",
643
- speed: "Speed:",
644
- play: "Play",
645
- pause: "Pause",
646
- resume: "Resume",
647
- stop: "Stop",
648
- speaking: "Speaking…",
649
- paused: "Paused",
650
- charactersSelected: {
651
- one: "{count} character selected",
652
- other: "{count} characters selected"
653
- },
654
- rate: {
655
- slow: "Slow",
656
- slower: "Slower",
657
- normal: "Normal",
658
- faster: "Faster",
659
- fast: "Fast",
660
- veryFast: "Very Fast"
661
- },
662
649
  inline: {
663
650
  controlsA11y: "Reading controls",
664
651
  playbackSpeedA11y: "Playback speed",
@@ -764,6 +751,7 @@ var fe = {
764
751
  resolvedTools: "Resolved Tools (toolkit)",
765
752
  provenanceSummary: "Tool Policy Provenance Summary",
766
753
  perToolDecisions: "Per-Tool Decisions",
754
+ policyDiagnostics: "Policy Diagnostics",
767
755
  profileReadOnly: "PNP Profile (read-only)"
768
756
  },
769
757
  tts: {
@@ -834,7 +822,7 @@ var fe = {
834
822
  coordinatorUnavailable: "Toolkit coordinator is not available for text-to-speech updates."
835
823
  }
836
824
  }
837
- }, k = "en-US", ye = /* @__PURE__ */ new Set([
825
+ }, k = "en-US", be = /* @__PURE__ */ new Set([
838
826
  "ar",
839
827
  "ckb",
840
828
  "dv",
@@ -850,7 +838,7 @@ var fe = {
850
838
  "ug",
851
839
  "ur",
852
840
  "yi"
853
- ]), ve = /* @__PURE__ */ new Set([
841
+ ]), Se = /* @__PURE__ */ new Set([
854
842
  "zero",
855
843
  "one",
856
844
  "two",
@@ -861,7 +849,7 @@ var fe = {
861
849
  function _(e) {
862
850
  if (typeof e != "object" || e === null) return !1;
863
851
  const t = Object.keys(e);
864
- return t.length > 0 && t.every((i) => ve.has(i));
852
+ return t.length > 0 && t.every((i) => Se.has(i));
865
853
  }
866
854
  function z(e) {
867
855
  try {
@@ -869,12 +857,12 @@ function z(e) {
869
857
  if (i?.direction === "rtl" || i?.direction === "ltr") return i.direction;
870
858
  } catch {
871
859
  }
872
- const t = w(e).split("-")[0] ?? "";
873
- return ye.has(t) ? "rtl" : "ltr";
860
+ const t = A(e).split("-")[0] ?? "";
861
+ return be.has(t) ? "rtl" : "ltr";
874
862
  }
875
- var G = class E {
863
+ var $ = class E {
876
864
  constructor(t = {}) {
877
- g(this, "locale", void 0), g(this, "fallbackLocale", void 0), g(this, "catalogs", void 0), g(this, "customMessages", void 0), g(this, "loading", void 0), g(this, "listeners", /* @__PURE__ */ new Set()), g(this, "config", void 0), this.config = { ...t }, this.fallbackLocale = C(t.fallbackLocale) || "en-US", this.catalogs = /* @__PURE__ */ new Map([[k, fe]]), this.customMessages = { ...t.customMessages ?? {} }, this.loading = /* @__PURE__ */ new Map(), this.locale = this.resolveLocaleTag(t.locale) ?? "en-US";
865
+ f(this, "locale", void 0), f(this, "fallbackLocale", void 0), f(this, "catalogs", void 0), f(this, "customMessages", void 0), f(this, "loading", void 0), f(this, "listeners", /* @__PURE__ */ new Set()), f(this, "config", void 0), this.config = { ...t }, this.fallbackLocale = P(t.fallbackLocale) || "en-US", this.catalogs = /* @__PURE__ */ new Map([[k, ve]]), this.customMessages = { ...t.customMessages ?? {} }, this.loading = /* @__PURE__ */ new Map(), this.locale = this.resolveLocaleTag(t.locale) ?? "en-US";
878
866
  }
879
867
  async initialize(t) {
880
868
  if (this.config = {
@@ -889,7 +877,7 @@ var G = class E {
889
877
  async setLocale(t) {
890
878
  const i = this.resolveLocaleTag(t);
891
879
  if (!i) {
892
- this.locale = C(t) || "en-US", this.notify();
880
+ this.locale = P(t) || "en-US", this.notify();
893
881
  return;
894
882
  }
895
883
  if (!this.catalogs.has(i)) {
@@ -938,11 +926,11 @@ var G = class E {
938
926
  return this.lookup(t) !== void 0;
939
927
  }
940
928
  addCustomMessages(t, i) {
941
- const s = C(t);
942
- this.customMessages[s] = $(this.customMessages[s] ?? {}, i), this.notify();
929
+ const s = P(t);
930
+ this.customMessages[s] = j(this.customMessages[s] ?? {}, i), this.notify();
943
931
  }
944
932
  withLocale(t) {
945
- const i = this.resolveLocaleTag(t) ?? C(t);
933
+ const i = this.resolveLocaleTag(t) ?? P(t);
946
934
  if (i === this.locale) return this;
947
935
  const s = new E({
948
936
  ...this.config,
@@ -977,11 +965,11 @@ var G = class E {
977
965
  }
978
966
  resolveLocaleTag(t) {
979
967
  if (!t) return;
980
- const i = this.getAvailableLocales(), s = ge(t, i);
968
+ const i = this.getAvailableLocales(), s = ye(t, i);
981
969
  if (s) return s;
982
- const n = w(t).split("-")[0];
970
+ const n = A(t).split("-")[0];
983
971
  if (n)
984
- return i.find((o) => w(o).split("-")[0] === n);
972
+ return i.find((o) => A(o).split("-")[0] === n);
985
973
  }
986
974
  async loadCatalog(t) {
987
975
  const i = this.config.loadCatalog;
@@ -1000,7 +988,7 @@ var G = class E {
1000
988
  this.catalogs.get(this.fallbackLocale)
1001
989
  ];
1002
990
  for (const s of i) {
1003
- const n = be(s, t);
991
+ const n = we(s, t);
1004
992
  if (n !== void 0) return n;
1005
993
  }
1006
994
  }
@@ -1025,11 +1013,11 @@ var G = class E {
1025
1013
  for (const t of this.listeners) t();
1026
1014
  }
1027
1015
  };
1028
- g(G, "pluralRules", /* @__PURE__ */ new Map());
1029
- function C(e) {
1016
+ f($, "pluralRules", /* @__PURE__ */ new Map());
1017
+ function P(e) {
1030
1018
  return e ? e.trim().replace(/_/g, "-") : "";
1031
1019
  }
1032
- function be(e, t) {
1020
+ function we(e, t) {
1033
1021
  if (!e) return;
1034
1022
  let i = e;
1035
1023
  for (const s of t.split("."))
@@ -1042,46 +1030,46 @@ function U(e, t) {
1042
1030
  return n == null ? i : String(n);
1043
1031
  }) : e;
1044
1032
  }
1045
- function $(e, t) {
1033
+ function j(e, t) {
1046
1034
  const i = { ...e };
1047
1035
  for (const [s, n] of Object.entries(t)) {
1048
1036
  const o = i[s];
1049
- n && typeof n == "object" && o && typeof o == "object" ? i[s] = $(o, n) : i[s] = n;
1037
+ n && typeof n == "object" && o && typeof o == "object" ? i[s] = j(o, n) : i[s] = n;
1050
1038
  }
1051
1039
  return i;
1052
1040
  }
1053
- function Se(e = {}) {
1054
- return new G({
1055
- loadCatalog: pe,
1056
- availableLocales: he,
1041
+ function Ae(e = {}) {
1042
+ return new $({
1043
+ loadCatalog: ge,
1044
+ availableLocales: me,
1057
1045
  ...e
1058
1046
  });
1059
1047
  }
1060
- function v() {
1048
+ function S() {
1061
1049
  return Date.now();
1062
1050
  }
1063
- function P(e) {
1051
+ function C(e) {
1064
1052
  return e.replace(/[^a-zA-Z0-9_-]/g, "-");
1065
1053
  }
1066
- function we(e) {
1067
- if (e.attemptId) return `aas_v1_${P(e.assessmentId)}_${P(e.attemptId)}`;
1068
- const t = j(), i = t ? Ae(t) : "anon";
1069
- return `aas_v1_${P(e.assessmentId)}_${P(i)}`;
1054
+ function Ie(e) {
1055
+ if (e.attemptId) return `aas_v1_${C(e.assessmentId)}_${C(e.attemptId)}`;
1056
+ const t = W(), i = t ? Pe(t) : "anon";
1057
+ return `aas_v1_${C(e.assessmentId)}_${C(i)}`;
1070
1058
  }
1071
- function j() {
1059
+ function W() {
1072
1060
  try {
1073
1061
  return typeof window > "u" ? null : window.localStorage;
1074
1062
  } catch {
1075
1063
  return null;
1076
1064
  }
1077
1065
  }
1078
- function Ae(e) {
1066
+ function Pe(e) {
1079
1067
  const t = "pie:assessment-player:anonymous-device-id", i = e.getItem(t);
1080
1068
  if (i) return i;
1081
1069
  const s = `anon-${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
1082
1070
  return e.setItem(t, s), s;
1083
1071
  }
1084
- function Ie(e) {
1072
+ function Ce(e) {
1085
1073
  if (!e) return [];
1086
1074
  const t = [], i = e.testParts || [];
1087
1075
  return i.length > 0 ? (i.forEach((s, n) => {
@@ -1104,12 +1092,12 @@ function Ie(e) {
1104
1092
  });
1105
1093
  }), t);
1106
1094
  }
1107
- var Ce = class {
1095
+ var Ee = class {
1108
1096
  constructor(e) {
1109
- this.args = e, this.listeners = /* @__PURE__ */ new Set(), this.deliveryPlan = { sections: [] }, this.session = null, this.readiness = "bootstrapping", this.submitted = !1, this.disposed = !1, this.storageContext = {
1097
+ this.args = e, this.listeners = /* @__PURE__ */ new Set(), this.deliveryPlan = { sections: [] }, this.session = null, this.readiness = "bootstrapping", this.submitted = !1, this.saveQueue = Promise.resolve(), this.disposed = !1, this.storageContext = {
1110
1098
  assessmentId: e.assessmentId,
1111
1099
  attemptId: e.attemptId
1112
- };
1100
+ }, this.initialSession = e.initialSession ? y(e.initialSession) : null;
1113
1101
  }
1114
1102
  emit(e) {
1115
1103
  if (!this.disposed)
@@ -1130,30 +1118,30 @@ var Ce = class {
1130
1118
  async getPersistenceStrategy() {
1131
1119
  if (this.persistenceStrategy) return this.persistenceStrategy;
1132
1120
  const e = { createDefaultPersistence: () => {
1133
- const t = j(), i = `pie:assessment-controller:v1:${this.storageContext.assessmentId}:${this.storageContext.attemptId || "default"}`;
1121
+ const t = W(), { assessmentId: i, attemptId: s } = this.storageContext, n = s ? `pie:assessment-controller:v1:${i}:${s}` : null;
1134
1122
  return {
1135
1123
  async loadSession() {
1136
- if (!t) return null;
1137
- const s = t.getItem(i);
1138
- if (!s) return null;
1124
+ if (!t || !n) return null;
1125
+ const o = t.getItem(n);
1126
+ if (!o) return null;
1139
1127
  try {
1140
- return JSON.parse(s);
1128
+ return JSON.parse(o);
1141
1129
  } catch {
1142
1130
  return null;
1143
1131
  }
1144
1132
  },
1145
- async saveSession(s, n) {
1146
- t && t.setItem(i, JSON.stringify(n));
1133
+ async saveSession(o, r) {
1134
+ !t || !n || t.setItem(n, JSON.stringify(r));
1147
1135
  },
1148
1136
  async clearSession() {
1149
- t && t.removeItem(i);
1137
+ !t || !n || t.removeItem(n);
1150
1138
  }
1151
1139
  };
1152
1140
  } };
1153
1141
  return this.persistenceStrategy = await this.args.hooks?.createAssessmentSessionPersistence?.(this.storageContext, e) ?? await e.createDefaultPersistence(), this.persistenceStrategy;
1154
1142
  }
1155
1143
  async createDeliveryPlan() {
1156
- const e = { createDefaultDeliveryPlan: () => ({ sections: Ie(this.args.assessment) }) }, t = {
1144
+ const e = { createDefaultDeliveryPlan: () => ({ sections: Ce(this.args.assessment) }) }, t = {
1157
1145
  assessmentId: this.args.assessmentId,
1158
1146
  attemptId: this.args.attemptId,
1159
1147
  assessment: this.args.assessment
@@ -1163,8 +1151,8 @@ var Ce = class {
1163
1151
  ensureSession() {
1164
1152
  if (this.session) return this.session;
1165
1153
  const e = this.deliveryPlan.sections.map((t) => t.sectionIdentifier);
1166
- return this.session = V({
1167
- assessmentAttemptSessionIdentifier: we({
1154
+ return this.session = Q({
1155
+ assessmentAttemptSessionIdentifier: Ie({
1168
1156
  assessmentId: this.args.assessmentId,
1169
1157
  attemptId: this.args.attemptId
1170
1158
  }),
@@ -1197,10 +1185,12 @@ var Ce = class {
1197
1185
  await this.args.hooks?.onBeforeAssessmentHydrate?.(this.storageContext), this.assertActive();
1198
1186
  const e = await this.getPersistenceStrategy();
1199
1187
  this.assertActive();
1200
- const t = await e.loadSession(this.storageContext);
1201
- this.assertActive(), t && (this.session = t), this.ensureSession(), this.readiness = "ready", t && this.emit({
1188
+ const t = this.initialSession;
1189
+ this.initialSession = null;
1190
+ const i = t ?? await e.loadSession(this.storageContext);
1191
+ this.assertActive(), i && (this.session = i), this.ensureSession(), this.readiness = "ready", i && this.emit({
1202
1192
  type: "assessment-session-applied",
1203
- timestamp: v()
1193
+ timestamp: S()
1204
1194
  }), this.assertActive();
1205
1195
  } catch (e) {
1206
1196
  try {
@@ -1223,13 +1213,19 @@ var Ce = class {
1223
1213
  async persist() {
1224
1214
  this.assertActive();
1225
1215
  try {
1226
- const e = await this.getPersistenceStrategy();
1227
- if (this.disposed || (await this.args.hooks?.onBeforeAssessmentPersist?.(this.storageContext, this.session), this.disposed)) return;
1228
- await e.saveSession(this.storageContext, this.session);
1216
+ await this.save();
1229
1217
  } catch (e) {
1230
1218
  this.disposed || this.handleError(e, "session-save");
1231
1219
  }
1232
1220
  }
1221
+ save() {
1222
+ const e = this.saveQueue.then(async () => {
1223
+ const t = await this.getPersistenceStrategy();
1224
+ this.disposed || (await this.args.hooks?.onBeforeAssessmentPersist?.(this.storageContext, this.session), !this.disposed && await t.saveSession(this.storageContext, this.session));
1225
+ });
1226
+ return this.saveQueue = e.catch(() => {
1227
+ }), e;
1228
+ }
1233
1229
  getSession() {
1234
1230
  return this.session;
1235
1231
  }
@@ -1261,14 +1257,14 @@ var Ce = class {
1261
1257
  const t = this.buildNavigationSnapshot();
1262
1258
  if (e < 0 || e >= t.totalSections) return !1;
1263
1259
  const i = t.currentSectionId, s = this.deliveryPlan.sections[e];
1264
- this.session = J(this.ensureSession(), {
1260
+ this.session = K(this.ensureSession(), {
1265
1261
  currentSectionIndex: e,
1266
1262
  currentSectionIdentifier: s?.sectionIdentifier
1267
1263
  });
1268
1264
  const n = this.buildNavigationSnapshot();
1269
1265
  return this.emit({
1270
1266
  type: "assessment-route-changed",
1271
- timestamp: v(),
1267
+ timestamp: S(),
1272
1268
  currentSectionIndex: n.currentSectionIndex,
1273
1269
  totalSections: n.totalSections,
1274
1270
  currentSectionId: n.currentSectionId,
@@ -1277,10 +1273,10 @@ var Ce = class {
1277
1273
  canPrevious: n.canPrevious
1278
1274
  }), this.disposed || (this.emit({
1279
1275
  type: "assessment-session-changed",
1280
- timestamp: v()
1276
+ timestamp: S()
1281
1277
  }), this.disposed) ? !1 : (this.emit({
1282
1278
  type: "assessment-progress-changed",
1283
- timestamp: v(),
1279
+ timestamp: S(),
1284
1280
  visitedSectionCount: this.ensureSession().navigationState.visitedSectionIdentifiers.length,
1285
1281
  totalSections: n.totalSections
1286
1282
  }), !0);
@@ -1299,11 +1295,17 @@ var Ce = class {
1299
1295
  return this.setIndex(e.currentSectionIndex - 1);
1300
1296
  }
1301
1297
  async submit() {
1302
- this.assertActive(), this.submitted = !0, this.emit({
1298
+ this.assertActive();
1299
+ try {
1300
+ await this.save();
1301
+ } catch (e) {
1302
+ throw this.disposed || this.handleError(e, "session-save"), e;
1303
+ }
1304
+ this.disposed || (this.submitted = !0, this.emit({
1303
1305
  type: "assessment-submission-state-changed",
1304
- timestamp: v(),
1306
+ timestamp: S(),
1305
1307
  submitted: !0
1306
- }), await this.persist();
1308
+ }));
1307
1309
  }
1308
1310
  subscribe(e) {
1309
1311
  return this.disposed ? () => {
@@ -1320,15 +1322,15 @@ var Ce = class {
1320
1322
  return this.disposed ? null : this.ensureSession().sectionSessions[e]?.session || null;
1321
1323
  }
1322
1324
  updateSectionSession(e, t) {
1323
- this.disposed || (this.session = K(this.ensureSession(), {
1325
+ this.disposed || (this.session = Y(this.ensureSession(), {
1324
1326
  sectionIdentifier: e,
1325
1327
  sectionSession: t
1326
1328
  }), this.emit({
1327
1329
  type: "assessment-session-changed",
1328
- timestamp: v()
1330
+ timestamp: S()
1329
1331
  }));
1330
1332
  }
1331
- }, p = {
1333
+ }, g = {
1332
1334
  controllerReady: "assessment-controller-ready",
1333
1335
  navigationRequested: "assessment-navigation-requested",
1334
1336
  routeChanged: "assessment-route-changed",
@@ -1338,7 +1340,7 @@ var Ce = class {
1338
1340
  submissionStateChanged: "assessment-submission-state-changed",
1339
1341
  error: "assessment-error"
1340
1342
  };
1341
- function Pe(e, t) {
1343
+ function ke(e, t) {
1342
1344
  return !e?.delivery || e.delivery.enabled === !1 || !t || e.delivery.assignmentId !== void 0 ? e : {
1343
1345
  ...e,
1344
1346
  delivery: {
@@ -1347,22 +1349,35 @@ function Pe(e, t) {
1347
1349
  }
1348
1350
  };
1349
1351
  }
1350
- function Ee(e) {
1351
- const { sectionPlayerRuntime: t, playerType: i, attemptId: s, env: n, coordinator: o } = e, r = t?.player && typeof t.player == "object" ? {
1352
+ function Re(e) {
1353
+ const { sectionPlayerRuntime: t, playerType: i, assessmentId: s, attemptId: n, env: o, coordinator: r } = e, a = t?.player && typeof t.player == "object" ? {
1352
1354
  ...t.player,
1353
- backend: Pe(t.player.backend ? b(t.player.backend) : void 0, s)
1355
+ backend: ke(t.player.backend ? y(t.player.backend) : void 0, n)
1354
1356
  } : t?.player;
1355
1357
  return {
1356
1358
  playerType: i,
1357
- ...n ? { env: n } : {},
1358
- ...o ? { coordinator: o } : {},
1359
+ ...s ? { assessmentId: s } : {},
1360
+ ...o ? { env: o } : {},
1361
+ ...r ? { coordinator: r } : {},
1359
1362
  ...t || {},
1360
- ...r !== void 0 ? { player: r } : {}
1363
+ ...a !== void 0 ? { player: a } : {}
1361
1364
  };
1362
1365
  }
1363
- var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe = class extends HTMLElement {
1366
+ function B(e) {
1367
+ return !!e && typeof e == "object";
1368
+ }
1369
+ function N(e, t) {
1370
+ if (Object.is(e, t)) return !0;
1371
+ if (Array.isArray(e) || Array.isArray(t))
1372
+ return !Array.isArray(e) || !Array.isArray(t) || e.length !== t.length ? !1 : e.every((s, n) => N(s, t[n]));
1373
+ if (!B(e) || !B(t)) return !1;
1374
+ const i = /* @__PURE__ */ new Set([...Object.keys(e), ...Object.keys(t)]);
1375
+ for (const s of i) if (!N(e[s], t[s])) return !1;
1376
+ return !0;
1377
+ }
1378
+ var Ne = "pie-section-player-splitpane", xe = "pie-section-player-vertical", Te = class extends HTMLElement {
1364
1379
  constructor(...e) {
1365
- super(...e), this._assessmentId = "", this._attemptId = "", this._locale = "", this._assessment = null, this._env = null, this._coordinator = null, this._hooks = null, this._showNavigation = !0, this._sectionPlayerLayout = "splitpane", this._playerType = "iife", this._debug = void 0, this.controller = null, this.initializingController = null, this.generation = 0, this.reconcileQueued = !1, this.controllerWaiters = /* @__PURE__ */ new Set(), this.i18n = Se(), this.sectionHost = null, this.sectionControllerRef = null, this.sectionSessionReady = !1, this.containerRef = null, this.announcer = null, this.restoreFocusOnRender = !1, this._sectionPlayerRuntime = null, this.readiness = { phase: "bootstrapping" };
1380
+ super(...e), this._assessmentId = "", this._attemptId = "", this._locale = "", this._assessment = null, this._env = null, this._coordinator = null, this._hooks = null, this.pendingSession = null, this._showNavigation = !0, this._sectionPlayerLayout = "splitpane", this._playerType = "iife", this._debug = void 0, this.controller = null, this.initializingController = null, this.generation = 0, this.reconcileQueued = !1, this.controllerWaiters = /* @__PURE__ */ new Set(), this.i18n = Ae(), this.sectionHost = null, this.sectionControllerRef = null, this.sectionSessionReady = !1, this.containerRef = null, this.announcer = null, this.restoreFocusOnRender = !1, this._sectionPlayerRuntime = null, this.readiness = { phase: "bootstrapping" };
1366
1381
  }
1367
1382
  static get observedAttributes() {
1368
1383
  return [
@@ -1399,6 +1414,13 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1399
1414
  set hooks(e) {
1400
1415
  this._hooks !== e && (this._hooks = e, this.queueReconciliation());
1401
1416
  }
1417
+ get session() {
1418
+ return this.controller ? y(this.controller.getSession()) : this.pendingSession;
1419
+ }
1420
+ set session(e) {
1421
+ const t = e ?? null;
1422
+ t !== this.pendingSession && (t && this.controller && N(t, this.controller.getSession()) || (this.pendingSession = t, this.queueReconciliation()));
1423
+ }
1402
1424
  get locale() {
1403
1425
  return this._locale;
1404
1426
  }
@@ -1498,67 +1520,68 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1498
1520
  return;
1499
1521
  }
1500
1522
  this.readiness = { phase: "bootstrapping" }, this.renderEmptyState();
1501
- const e = this.generation, t = this.hooks, i = () => this.isConnected && this.generation === e, s = new Ce({
1523
+ const e = this.generation, t = this.hooks, i = () => this.isConnected && this.generation === e, s = this.pendingSession, n = new Ee({
1502
1524
  assessmentId: this.assessmentId,
1503
1525
  attemptId: this.attemptId || void 0,
1504
1526
  assessment: this.assessment,
1527
+ initialSession: s,
1505
1528
  hooks: {
1506
1529
  ...t,
1507
- onError: (n, o) => {
1530
+ onError: (o, r) => {
1508
1531
  if (i())
1509
1532
  try {
1510
- t?.onError?.(n, o);
1533
+ t?.onError?.(o, r);
1511
1534
  } finally {
1512
- i() && o.phase === "session-load" && this.controller === s && (this.retireController(), this.readiness = { phase: "error" }, this.renderEmptyState(), this.dispatch(p.error, { error: n }));
1535
+ i() && r.phase === "session-load" && this.controller === n && (this.retireController(), this.readiness = { phase: "error" }, this.renderEmptyState(), this.dispatch(g.error, { error: o }));
1513
1536
  }
1514
1537
  }
1515
1538
  }
1516
1539
  });
1517
- this.initializingController = s, this.unsubscribeController = s.subscribe((n) => {
1540
+ this.initializingController = n, this.unsubscribeController = n.subscribe((o) => {
1518
1541
  if (i()) {
1519
- if (n.type === "assessment-route-changed") {
1520
- if (this.syncCurrentSectionSessionIntoAssessment(), !i() || (this.cleanupTtsForSectionNavigation(n), !i()) || (this.dispatch(p.routeChanged, n), !i())) return;
1542
+ if (o.type === "assessment-route-changed") {
1543
+ if (this.syncCurrentSectionSessionIntoAssessment(), !i() || (this.cleanupTtsForSectionNavigation(o), !i()) || (this.dispatch(g.routeChanged, o), !i())) return;
1521
1544
  this.restoreFocusOnRender = !0, this.render(!1);
1522
1545
  }
1523
- n.type === "assessment-session-applied" && this.dispatch(p.sessionApplied, n), n.type === "assessment-session-changed" && this.dispatch(p.sessionChanged, n), n.type === "assessment-progress-changed" && this.dispatch(p.progressChanged, n), n.type === "assessment-submission-state-changed" && this.dispatch(p.submissionStateChanged, n);
1546
+ o.type === "assessment-session-applied" && this.dispatch(g.sessionApplied, o), o.type === "assessment-session-changed" && this.dispatch(g.sessionChanged, o), o.type === "assessment-progress-changed" && this.dispatch(g.progressChanged, o), o.type === "assessment-submission-state-changed" && this.dispatch(g.submissionStateChanged, o);
1524
1547
  }
1525
1548
  });
1526
1549
  try {
1527
- if (await s.initialize(), !i()) return;
1528
- this.initializingController = null, this.controller = s, this.readiness = { phase: "ready" }, this.render(), this.settleControllerWaiters(s);
1529
- const n = (o) => {
1550
+ if (await n.initialize(), !i()) return;
1551
+ this.pendingSession === s && (this.pendingSession = null), this.initializingController = null, this.controller = n, this.readiness = { phase: "ready" }, this.render(), this.settleControllerWaiters(n);
1552
+ const o = (r) => {
1530
1553
  if (i()) {
1531
1554
  try {
1532
- t?.onError?.(o instanceof Error ? o : new Error(String(o)), { phase: "controller-init" });
1533
- } catch (r) {
1534
- console.error("Assessment error hook failed", r);
1555
+ t?.onError?.(r instanceof Error ? r : new Error(String(r)), { phase: "controller-init" });
1556
+ } catch (a) {
1557
+ console.error("Assessment error hook failed", a);
1535
1558
  }
1536
- i() && this.dispatch(p.error, { error: o });
1559
+ i() && this.dispatch(g.error, { error: r });
1537
1560
  }
1538
1561
  };
1539
1562
  try {
1540
- Promise.resolve(t?.onAssessmentControllerReady?.(s)).catch(n);
1541
- } catch (o) {
1542
- n(o);
1563
+ Promise.resolve(t?.onAssessmentControllerReady?.(n)).catch(o);
1564
+ } catch (r) {
1565
+ o(r);
1543
1566
  }
1544
- i() && this.dispatch(p.controllerReady, { controller: s });
1545
- } catch (n) {
1567
+ i() && this.dispatch(g.controllerReady, { controller: n });
1568
+ } catch (o) {
1546
1569
  if (!i()) return;
1547
- this.retireController(), this.readiness = { phase: "error" }, this.renderEmptyState(), this.dispatch(p.error, { error: n });
1570
+ this.retireController(), this.readiness = { phase: "error" }, this.renderEmptyState(), this.dispatch(g.error, { error: o });
1548
1571
  }
1549
1572
  }
1550
1573
  resolveInstrumentationProvider() {
1551
- return ce({
1574
+ return ue({
1552
1575
  runtimePlayer: this.sectionPlayerRuntime?.player,
1553
1576
  component: "pie-assessment-player-default"
1554
1577
  });
1555
1578
  }
1556
1579
  attachInstrumentationBridge() {
1557
- this.detachInstrumentationBridge?.(), this.detachInstrumentationBridge = se({
1580
+ this.detachInstrumentationBridge?.(), this.detachInstrumentationBridge = oe({
1558
1581
  host: this,
1559
1582
  instrumentationProvider: this.resolveInstrumentationProvider(),
1560
1583
  component: "pie-assessment-player-default",
1561
- eventMap: de,
1584
+ eventMap: he,
1562
1585
  staticAttributes: {
1563
1586
  instrumentationLayer: "assessment",
1564
1587
  assessmentId: this.assessmentId,
@@ -1631,12 +1654,13 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1631
1654
  return;
1632
1655
  }
1633
1656
  buildSectionPlayerTag() {
1634
- return this.sectionPlayerLayout === "vertical" ? Re : ke;
1657
+ return this.sectionPlayerLayout === "vertical" ? xe : Ne;
1635
1658
  }
1636
1659
  buildSectionRuntime() {
1637
- return Ee({
1660
+ return Re({
1638
1661
  sectionPlayerRuntime: this.sectionPlayerRuntime,
1639
1662
  playerType: this.playerType,
1663
+ assessmentId: this.assessmentId || void 0,
1640
1664
  attemptId: this.attemptId || void 0,
1641
1665
  env: this.env,
1642
1666
  coordinator: this.coordinator
@@ -1651,7 +1675,7 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1651
1675
  const t = this.coordinator, i = t?.ttsService;
1652
1676
  if (!i) return;
1653
1677
  const s = (n) => {
1654
- t?.reportFrameworkError?.(Q({
1678
+ t?.reportFrameworkError?.(J({
1655
1679
  kind: "tts-init",
1656
1680
  source: "AssessmentPlayerDefaultElement.cleanupTtsForSectionNavigation",
1657
1681
  error: n,
@@ -1675,38 +1699,34 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1675
1699
  const t = this.sectionControllerRef;
1676
1700
  if (!t?.getSession) return;
1677
1701
  const i = this.sectionHost?.firstElementChild?.getAttribute("section-id");
1678
- i && e.updateSectionSession(i, b(t.getSession()));
1702
+ i && e.updateSectionSession(i, y(t.getSession()));
1679
1703
  }
1680
- attachSectionControllerReadyListener(e, t) {
1681
- const i = e, s = this.generation, n = this.sectionHost, o = this.controller, r = b(o.getSectionSession(t)), a = () => this.isConnected && this.generation === s && this.sectionHost === n && this.controller === o;
1682
- n.setAttribute("aria-busy", "true"), i.inert = !0;
1704
+ attachSectionControllerReadyListener(e, t, i) {
1705
+ const s = e, n = this.generation, o = this.sectionHost, r = this.controller, a = () => this.isConnected && this.generation === n && this.sectionHost === o && this.controller === r;
1706
+ o.setAttribute("aria-busy", "true"), s.inert = !0;
1683
1707
  let h;
1684
1708
  new Promise((c) => {
1685
- let m = !1;
1686
- const u = (A) => {
1687
- m || (m = !0, clearTimeout(f), i.removeEventListener(N.stageChange, l), c(A));
1688
- }, l = (A) => {
1689
- if (A.target !== i) return;
1690
- const { stage: x, status: W } = A.detail;
1691
- W === "failed" || x === "disposed" ? u(null) : x === "engine-ready" && u(i.getSectionController?.() || null);
1692
- }, f = setTimeout(() => u(null), 5e3);
1693
- i.addEventListener(N.stageChange, l), h = () => u(null);
1709
+ let p = !1;
1710
+ const u = (v) => {
1711
+ p || (p = !0, clearTimeout(m), s.removeEventListener("pie-stage-change", l), c(v));
1712
+ }, l = (v) => {
1713
+ if (v.target !== s) return;
1714
+ const { stage: x, status: V } = v.detail;
1715
+ V === "failed" || x === "disposed" ? u(null) : x === "engine-ready" && u(s.getSectionController?.() || null);
1716
+ }, m = setTimeout(() => u(null), 5e3);
1717
+ s.addEventListener("pie-stage-change", l), h = () => u(null);
1694
1718
  }).then(async (c) => {
1695
1719
  if (a()) {
1696
1720
  if (!c) throw new Error("Section controller did not become ready");
1697
- if (r) {
1698
- if (!c.applySession) throw new Error("Section controller cannot restore its saved session");
1699
- await c.applySession(r, { mode: "replace" });
1700
- }
1701
- a() && (this.sectionControllerRef = c, this.sectionSessionReady = !0, i.inert = !1, n.setAttribute("aria-busy", "false"));
1721
+ a() && (this.sectionControllerRef = c, this.sectionSessionReady = !0, s.inert = !1, o.setAttribute("aria-busy", "false"));
1702
1722
  }
1703
1723
  }).catch((c) => {
1704
1724
  if (!a()) return;
1705
1725
  this.detachSectionListeners?.(), this.detachSectionListeners = void 0, this.sectionControllerRef = null, this.sectionSessionReady = !1;
1706
- const m = n.contains(document.activeElement), u = this.createStatusContent(r ? "player.assessment.restoreFailed" : "player.assessment.loadFailed", () => {
1726
+ const p = o.contains(document.activeElement), u = this.createStatusContent(i ? "player.assessment.restoreFailed" : "player.assessment.loadFailed", () => {
1707
1727
  this.restoreFocusOnRender = this.contains(document.activeElement), this.render();
1708
1728
  });
1709
- n.replaceChildren(u), n.setAttribute("aria-busy", "false"), m && u.focus();
1729
+ o.replaceChildren(u), o.setAttribute("aria-busy", "false"), p && u.focus();
1710
1730
  try {
1711
1731
  this.hooks?.onError?.(c instanceof Error ? c : new Error(String(c)), {
1712
1732
  phase: "navigation",
@@ -1715,7 +1735,7 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1715
1735
  } catch (l) {
1716
1736
  console.error("Assessment error hook failed", l);
1717
1737
  }
1718
- a() && this.dispatch(p.error, { error: c });
1738
+ a() && this.dispatch(g.error, { error: c });
1719
1739
  });
1720
1740
  const d = () => {
1721
1741
  a() && this.syncCurrentSectionSessionIntoAssessment();
@@ -1757,7 +1777,7 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1757
1777
  padding: 0.5rem;
1758
1778
  border: 1px solid var(--pie-border-light, #e5e7eb);
1759
1779
  border-radius: 0.375rem;
1760
- background: var(--pie-background-light, var(--pie-background, #fff));
1780
+ background: var(--pie-background, #fff);
1761
1781
  }
1762
1782
  .pie-assessment-player-navigation[hidden] { display: none; }
1763
1783
  .pie-assessment-player-current-position {
@@ -1772,7 +1792,7 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1772
1792
  padding: 0.35rem 0.75rem;
1773
1793
  border: 1px solid var(--pie-border-light, #e5e7eb);
1774
1794
  border-radius: 0.375rem;
1775
- background: var(--pie-background-light, var(--pie-background, #fff));
1795
+ background: var(--pie-background, #fff);
1776
1796
  cursor: pointer;
1777
1797
  }
1778
1798
  .pie-assessment-player-nav-btn:disabled {
@@ -1795,23 +1815,24 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1795
1815
  u.className = "pie-assessment-player-navigation", u.hidden = !R(this.showNavigation, !0);
1796
1816
  const l = document.createElement("div");
1797
1817
  l.className = "pie-assessment-player-current-position", l.textContent = a;
1798
- const f = document.createElement("div");
1799
- f.className = "pie-assessment-player-nav-controls", h = document.createElement("button"), h.type = "button", h.className = "pie-assessment-player-nav-btn", h.setAttribute("data-pie-navigation", "previous"), h.textContent = this.i18n.t("common.back"), h.disabled = !r.navigation.canPrevious, h.addEventListener("click", () => {
1818
+ const m = document.createElement("div");
1819
+ m.className = "pie-assessment-player-nav-controls", h = document.createElement("button"), h.type = "button", h.className = "pie-assessment-player-nav-btn", h.setAttribute("data-pie-navigation", "previous"), h.textContent = this.i18n.t("common.back"), h.disabled = !r.navigation.canPrevious, h.addEventListener("click", () => {
1800
1820
  this.navigatePrevious();
1801
1821
  }), d = document.createElement("button"), d.type = "button", d.className = "pie-assessment-player-nav-btn", d.setAttribute("data-pie-navigation", "next"), d.textContent = this.i18n.t("common.next"), d.disabled = !r.navigation.canNext, d.addEventListener("click", () => {
1802
1822
  this.navigateNext();
1803
- }), f.appendChild(h), f.appendChild(d), u.appendChild(l), u.appendChild(f), n.appendChild(u);
1823
+ }), m.appendChild(h), m.appendChild(d), u.appendChild(l), u.appendChild(m), n.appendChild(u);
1804
1824
  }
1805
1825
  const c = document.createElement("div");
1806
1826
  c.className = "pie-assessment-player-section-host", c.setAttribute("role", "region"), c.setAttribute("aria-label", a), c.tabIndex = -1, this.sectionHost = c;
1807
- const m = i?.getCurrentSection() || null;
1808
- if (m) {
1827
+ const p = i?.getCurrentSection() || null;
1828
+ if (p) {
1809
1829
  const u = this.buildSectionPlayerTag(), l = document.createElement(u);
1810
- if (l.setAttribute("assessment-id", this.assessmentId), l.setAttribute("section-id", m.sectionIdentifier), this.attemptId && l.setAttribute("attempt-id", this.attemptId), this.locale && l.setAttribute("locale", this.locale), this.debug !== void 0 && this.debug !== null) {
1811
- const f = typeof this.debug == "boolean" ? String(this.debug) : this.debug;
1812
- l.setAttribute("debug", f);
1830
+ if (l.setAttribute("section-id", p.sectionIdentifier), this.attemptId && l.setAttribute("attempt-id", this.attemptId), this.locale && l.setAttribute("locale", this.locale), this.debug !== void 0 && this.debug !== null) {
1831
+ const v = typeof this.debug == "boolean" ? String(this.debug) : this.debug;
1832
+ l.setAttribute("debug", v);
1813
1833
  }
1814
- l.section = m.section, l.runtime = this.buildSectionRuntime(), l.hooks = { cardTitleFormatter: this.hooks?.cardTitleFormatter }, c.appendChild(l), this.attachSectionControllerReadyListener(l, m.sectionIdentifier);
1834
+ const m = y(i.getSectionSession(p.sectionIdentifier));
1835
+ l.section = p.section, m && (l.session = m), l.runtime = this.buildSectionRuntime(), l.hooks = { cardTitleFormatter: this.hooks?.cardTitleFormatter }, c.appendChild(l), this.attachSectionControllerReadyListener(l, p.sectionIdentifier, !!m);
1815
1836
  }
1816
1837
  n.appendChild(c), this.swapContent(n), s && (this.applyFocusIntent(s, {
1817
1838
  previous: h,
@@ -1886,7 +1907,7 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1886
1907
  if (typeof e == "number")
1887
1908
  n = e, o = this.controller?.getSectionAt(n)?.sectionIdentifier;
1888
1909
  else if (o = e, this.controller?.getCurrentSection()?.sectionIdentifier === o) return !0;
1889
- if (!this.dispatch(p.navigationRequested, {
1910
+ if (!this.dispatch(g.navigationRequested, {
1890
1911
  fromIndex: i,
1891
1912
  toIndex: n,
1892
1913
  fromSectionId: s,
@@ -1898,7 +1919,7 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1898
1919
  }
1899
1920
  navigateNext() {
1900
1921
  const e = this.selectNavigation(), t = this.controller?.getSectionAt(e.currentIndex + 1);
1901
- if (!this.dispatch(p.navigationRequested, {
1922
+ if (!this.dispatch(g.navigationRequested, {
1902
1923
  fromIndex: e.currentIndex,
1903
1924
  toIndex: e.currentIndex + 1,
1904
1925
  fromSectionId: e.currentSectionId,
@@ -1910,7 +1931,7 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1910
1931
  }
1911
1932
  navigatePrevious() {
1912
1933
  const e = this.selectNavigation(), t = this.controller?.getSectionAt(e.currentIndex - 1);
1913
- if (!this.dispatch(p.navigationRequested, {
1934
+ if (!this.dispatch(g.navigationRequested, {
1914
1935
  fromIndex: e.currentIndex,
1915
1936
  toIndex: e.currentIndex - 1,
1916
1937
  fromSectionId: e.currentSectionId,
@@ -1933,8 +1954,8 @@ var ke = "pie-section-player-splitpane", Re = "pie-section-player-vertical", xe
1933
1954
  }));
1934
1955
  }
1935
1956
  };
1936
- B("pie-assessment-player-default", xe, "assessment player default tag");
1937
- var Ne = class extends HTMLElement {
1957
+ O("pie-assessment-player-default", Te, "assessment player default tag");
1958
+ var Le = class extends HTMLElement {
1938
1959
  constructor(...e) {
1939
1960
  super(...e), this.showNavigation = !0;
1940
1961
  }
@@ -1980,7 +2001,7 @@ var Ne = class extends HTMLElement {
1980
2001
  s.appendChild(n), i.appendChild(s), this.appendChild(t), this.appendChild(i);
1981
2002
  }
1982
2003
  };
1983
- B("pie-assessment-player-shell", Ne, "assessment player shell tag");
2004
+ O("pie-assessment-player-shell", Le, "assessment player shell tag");
1984
2005
  export {
1985
- p as ASSESSMENT_PLAYER_PUBLIC_EVENTS
2006
+ g as ASSESSMENT_PLAYER_PUBLIC_EVENTS
1986
2007
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-players/pie-assessment-player",
3
- "version": "0.3.74",
3
+ "version": "0.3.75",
4
4
  "type": "module",
5
5
  "description": "Assessment-level coordinator custom elements for orchestrating section-player instances",
6
6
  "license": "MIT",
@@ -45,14 +45,14 @@
45
45
  "build": "vite build",
46
46
  "dev": "vite build --watch",
47
47
  "test": "bun test",
48
- "typecheck": "tsc --noEmit",
48
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.tests.json",
49
49
  "check": "tsc --noEmit",
50
50
  "lint": "biome lint ."
51
51
  },
52
52
  "dependencies": {
53
- "@pie-players/pie-assessment-toolkit": "0.3.74",
54
- "@pie-players/pie-players-shared": "0.3.74",
55
- "@pie-players/pie-section-player": "0.3.74"
53
+ "@pie-players/pie-assessment-toolkit": "0.3.75",
54
+ "@pie-players/pie-players-shared": "0.3.75",
55
+ "@pie-players/pie-section-player": "0.3.75"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@axe-core/playwright": "^4.13.0",
@@ -60,7 +60,7 @@
60
60
  "@biomejs/biome": "^2.5.15",
61
61
  "typescript": "^5.9.3",
62
62
  "vite": "^8.3.2",
63
- "vite-plugin-dts": "^5.1.1"
63
+ "vite-plugin-dts": "^5.1.2"
64
64
  },
65
65
  "homepage": "https://github.com/pie-framework/pie-players/tree/master/packages/assessment-player#readme",
66
66
  "bugs": {