@pie-players/pie-players-shared 0.3.73 → 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.
Files changed (137) hide show
  1. package/README.md +30 -2
  2. package/dist/components/vendor/nds/README.md +7 -5
  3. package/dist/config/profile.d.ts +0 -2
  4. package/dist/config/profile.js +2 -8
  5. package/dist/i18n/index.d.ts +2 -3
  6. package/dist/i18n/index.js +2 -3
  7. package/dist/i18n/messages/en-US.d.ts +11 -24
  8. package/dist/i18n/messages/en-US.js +13 -26
  9. package/dist/i18n/messages/nl-NL.d.ts +7 -24
  10. package/dist/i18n/messages/nl-NL.js +9 -26
  11. package/dist/i18n/provider.d.ts +4 -5
  12. package/dist/i18n/provider.js +4 -5
  13. package/dist/i18n/types.d.ts +4 -5
  14. package/dist/index.d.ts +4 -2
  15. package/dist/index.js +3 -1
  16. package/dist/instrumentation/index.d.ts +5 -4
  17. package/dist/instrumentation/index.js +5 -4
  18. package/dist/instrumentation/new-relic-agent.d.ts +20 -0
  19. package/dist/instrumentation/new-relic-agent.js +24 -0
  20. package/dist/instrumentation/providers/BaseInstrumentationProvider.d.ts +5 -4
  21. package/dist/instrumentation/providers/NewRelicInstrumentationProvider.d.ts +7 -7
  22. package/dist/instrumentation/providers/NewRelicInstrumentationProvider.js +21 -32
  23. package/dist/instrumentation/types.d.ts +7 -3
  24. package/dist/loader-config.d.ts +16 -6
  25. package/dist/loaders/controller-loading.d.ts +14 -0
  26. package/dist/loaders/controller-loading.js +12 -0
  27. package/dist/loaders/defaults.d.ts +10 -0
  28. package/dist/loaders/defaults.js +10 -0
  29. package/dist/loaders/element-loader.d.ts +20 -8
  30. package/dist/loaders/element-loader.js +102 -36
  31. package/dist/loaders/element-package-policy.d.ts +2 -0
  32. package/dist/loaders/element-package-policy.js +2 -1
  33. package/dist/loaders/esm-adapter.d.ts +29 -0
  34. package/dist/loaders/esm-adapter.js +635 -102
  35. package/dist/loaders/iife-adapter.d.ts +0 -5
  36. package/dist/loaders/iife-adapter.js +3 -9
  37. package/dist/loaders/index.d.ts +12 -3
  38. package/dist/loaders/index.js +9 -3
  39. package/dist/loaders/mathjax-events.d.ts +22 -0
  40. package/dist/loaders/mathjax-events.js +65 -0
  41. package/dist/loaders/module-shim.d.ts +12 -0
  42. package/dist/loaders/module-shim.js +12 -0
  43. package/dist/loaders/preloaded-alignment.d.ts +24 -0
  44. package/dist/loaders/preloaded-alignment.js +57 -0
  45. package/dist/loaders/preloaded-authored-tags.d.ts +26 -0
  46. package/dist/loaders/preloaded-authored-tags.js +69 -0
  47. package/dist/loaders/preloaded-registration.d.ts +72 -0
  48. package/dist/loaders/preloaded-registration.js +141 -0
  49. package/dist/media/index.d.ts +69 -0
  50. package/dist/media/index.js +194 -0
  51. package/dist/pie/authoring.d.ts +1 -1
  52. package/dist/pie/authoring.js +25 -18
  53. package/dist/pie/element-announcements.d.ts +39 -0
  54. package/dist/pie/element-announcements.js +67 -0
  55. package/dist/pie/element-observer.d.ts +3 -1
  56. package/dist/pie/element-observer.js +1 -0
  57. package/dist/pie/elements-rendered.d.ts +25 -0
  58. package/dist/pie/elements-rendered.js +96 -0
  59. package/dist/pie/index.d.ts +8 -10
  60. package/dist/pie/index.js +9 -10
  61. package/dist/pie/initialization.js +13 -22
  62. package/dist/pie/initialize-element.d.ts +3 -0
  63. package/dist/pie/initialize-element.js +3 -4
  64. package/dist/pie/instrumentation-event-bridge.js +23 -1
  65. package/dist/pie/instrumentation-event-map.js +11 -25
  66. package/dist/pie/instrumentation-provider-resolution.d.ts +2 -1
  67. package/dist/pie/instrumentation-provider-resolution.js +0 -1
  68. package/dist/pie/item-controller.js +4 -0
  69. package/dist/pie/item-session-contract.d.ts +8 -0
  70. package/dist/pie/item-session-contract.js +30 -9
  71. package/dist/pie/logger.d.ts +6 -0
  72. package/dist/pie/logger.js +10 -0
  73. package/dist/pie/markup-math.d.ts +20 -0
  74. package/dist/pie/markup-math.js +118 -0
  75. package/dist/pie/math-assets.d.ts +48 -0
  76. package/dist/pie/math-assets.js +81 -0
  77. package/dist/pie/math-rendering.d.ts +10 -21
  78. package/dist/pie/math-rendering.js +50 -35
  79. package/dist/pie/registry.d.ts +13 -1
  80. package/dist/pie/registry.js +39 -1
  81. package/dist/pie/resource-monitor.d.ts +6 -33
  82. package/dist/pie/resource-monitor.js +14 -213
  83. package/dist/pie/scoring.d.ts +12 -8
  84. package/dist/pie/scoring.js +21 -24
  85. package/dist/pie/session-commit.d.ts +16 -0
  86. package/dist/pie/session-commit.js +34 -6
  87. package/dist/pie/session-snapshot.d.ts +80 -0
  88. package/dist/pie/session-snapshot.js +166 -0
  89. package/dist/pie/stages.d.ts +10 -47
  90. package/dist/pie/stages.js +10 -47
  91. package/dist/pie/tag-names.d.ts +1 -0
  92. package/dist/pie/tag-names.js +1 -0
  93. package/dist/pie/types.d.ts +4 -5
  94. package/dist/pie/types.js +1 -0
  95. package/dist/pie/updates.d.ts +2 -2
  96. package/dist/pie/updates.js +33 -15
  97. package/dist/pie/versioned-tag.d.ts +4 -0
  98. package/dist/pie/versioned-tag.js +4 -0
  99. package/dist/security/index.d.ts +2 -3
  100. package/dist/security/index.js +2 -3
  101. package/dist/security/sanitize-item-markup.d.ts +4 -2
  102. package/dist/security/sanitize-item-markup.js +48 -6
  103. package/dist/security/sanitize-svg-icon.d.ts +0 -2
  104. package/dist/security/sanitize-svg-icon.js +0 -5
  105. package/dist/security/wrap-overwide-images.js +19 -0
  106. package/dist/security/wrap-overwide.d.ts +13 -0
  107. package/dist/security/wrap-overwide.js +63 -3
  108. package/dist/timed-media/policy.js +5 -16
  109. package/dist/types/index.d.ts +67 -423
  110. package/dist/types/index.js +0 -68
  111. package/dist/ui/attribute-coercion.d.ts +12 -0
  112. package/dist/ui/attribute-coercion.js +33 -0
  113. package/dist/ui/content-styles.d.ts +18 -16
  114. package/dist/ui/content-styles.js +67 -36
  115. package/dist/ui/overlay-containment.d.ts +31 -0
  116. package/dist/ui/overlay-containment.js +63 -0
  117. package/dist/ui/overlay-placement.d.ts +110 -0
  118. package/dist/ui/overlay-placement.js +222 -0
  119. package/dist/ui/pointer-gesture.d.ts +32 -0
  120. package/dist/ui/pointer-gesture.js +56 -0
  121. package/dist/ui/pointer-rotate.d.ts +29 -0
  122. package/dist/ui/pointer-rotate.js +39 -0
  123. package/package.json +23 -18
  124. package/dist/pie/component-context.d.ts +0 -21
  125. package/dist/pie/component-context.js +0 -29
  126. package/dist/pie/player-initializer.d.ts +0 -54
  127. package/dist/pie/player-initializer.js +0 -122
  128. package/dist/pie/stage-tracker.d.ts +0 -50
  129. package/dist/pie/stage-tracker.js +0 -105
  130. package/dist/security/wrap-model-rich-content.d.ts +0 -1
  131. package/dist/security/wrap-model-rich-content.js +0 -40
  132. package/dist/server/npm-auth-env.d.ts +0 -13
  133. package/dist/server/npm-auth-env.js +0 -64
  134. package/dist/server/npm-registry.d.ts +0 -7
  135. package/dist/server/npm-registry.js +0 -59
  136. package/dist/ui/debug-panel-persistence.d.ts +0 -17
  137. package/dist/ui/debug-panel-persistence.js +0 -37
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Device-local snapshot of a committed session.
3
+ *
4
+ * A crash or an OS kill fires no lifecycle event, so the only state that
5
+ * survives it is state already written. The snapshot writes each committed
6
+ * session to device storage and offers it back on the next load.
7
+ *
8
+ * It is offered, never applied. School devices are shared, and a player cannot
9
+ * tell a legitimate recovery from a previous student's draft, so the host
10
+ * decides.
11
+ *
12
+ * `sessionStorage` is the default, matching the annotation toolbar's existing
13
+ * use of it for learner annotations: it is tab-scoped and cleared when the tab
14
+ * closes, so a response does not outlive the sitting, and browser session
15
+ * restore brings it back after a crash. A host that wants recovery across a
16
+ * full browser restart injects a `localStorage`-backed store and owns the
17
+ * retention consequences.
18
+ */
19
+ const KEY_PREFIX = "pie-session-snapshot";
20
+ /**
21
+ * Whether an identity names one sitting.
22
+ *
23
+ * `sessionId` is the only field that distinguishes one learner's attempt from
24
+ * another's. Keyed on the item alone, a shared device offers the previous
25
+ * student's draft to the next one - and the offer is itself a disclosure, before
26
+ * the host ever decides whether to apply it. So without a `sessionId` there is
27
+ * no snapshot, and a host driving the player by props alone opts in by passing
28
+ * an explicit `key`.
29
+ */
30
+ export function isSnapshotIdentitySpecific(identity) {
31
+ return Boolean(identity.sessionId);
32
+ }
33
+ /**
34
+ * Key built from the delivery identity, so a snapshot is only offered back to
35
+ * the sitting that wrote it.
36
+ */
37
+ export function sessionSnapshotKey(identity) {
38
+ const parts = [
39
+ identity.assignmentId || "",
40
+ identity.sessionId || "",
41
+ identity.itemId || "",
42
+ ];
43
+ return `${KEY_PREFIX}:${parts.join("|")}`;
44
+ }
45
+ /**
46
+ * `sessionStorage`-backed store. Every access is guarded: private mode and
47
+ * blocked site data make the accessor throw, and a player must keep working
48
+ * without a snapshot rather than fail.
49
+ */
50
+ export function sessionStorageSnapshotStore() {
51
+ return {
52
+ read(key) {
53
+ try {
54
+ if (typeof sessionStorage === "undefined")
55
+ return null;
56
+ return sessionStorage.getItem(key);
57
+ }
58
+ catch {
59
+ return null;
60
+ }
61
+ },
62
+ write(key, value) {
63
+ try {
64
+ if (typeof sessionStorage === "undefined")
65
+ return;
66
+ sessionStorage.setItem(key, value);
67
+ }
68
+ catch {
69
+ // Quota, private mode, blocked site data: no snapshot this sitting.
70
+ }
71
+ },
72
+ clear(key) {
73
+ try {
74
+ if (typeof sessionStorage === "undefined")
75
+ return;
76
+ sessionStorage.removeItem(key);
77
+ }
78
+ catch {
79
+ // ignore
80
+ }
81
+ },
82
+ };
83
+ }
84
+ export function isSessionSnapshotEnabled(config) {
85
+ if (typeof config === "boolean")
86
+ return config;
87
+ if (!config)
88
+ return false;
89
+ return config.enabled !== false;
90
+ }
91
+ /**
92
+ * Returns `null` when the host has not enabled a snapshot, and when the
93
+ * identity does not name one sitting. PIE writes learner responses to the
94
+ * device only on an explicit opt-in, and only under a key no other learner
95
+ * shares.
96
+ */
97
+ export function createSessionSnapshot(args) {
98
+ if (!isSessionSnapshotEnabled(args.config))
99
+ return null;
100
+ const settings = typeof args.config === "object" && args.config ? args.config : {};
101
+ if (!settings.key && !isSnapshotIdentitySpecific(args.identity))
102
+ return null;
103
+ const key = settings.key || sessionSnapshotKey(args.identity);
104
+ const store = settings.store || sessionStorageSnapshotStore();
105
+ // A host-injected store is guarded the same way the default one is: a player
106
+ // keeps working without a snapshot rather than failing on storage.
107
+ const safeRead = () => {
108
+ try {
109
+ return store.read(key);
110
+ }
111
+ catch {
112
+ return null;
113
+ }
114
+ };
115
+ const safeWrite = (value) => {
116
+ try {
117
+ store.write(key, value);
118
+ }
119
+ catch {
120
+ // no snapshot this sitting
121
+ }
122
+ };
123
+ const safeClear = () => {
124
+ try {
125
+ store.clear(key);
126
+ }
127
+ catch {
128
+ // no snapshot this sitting
129
+ }
130
+ };
131
+ return {
132
+ key,
133
+ write(session) {
134
+ let payload;
135
+ try {
136
+ payload = JSON.stringify({ session, timestamp: Date.now() });
137
+ }
138
+ catch {
139
+ return;
140
+ }
141
+ safeWrite(payload);
142
+ },
143
+ read() {
144
+ const raw = safeRead();
145
+ if (!raw)
146
+ return null;
147
+ try {
148
+ const parsed = JSON.parse(raw);
149
+ if (!parsed || typeof parsed !== "object" || !("session" in parsed)) {
150
+ return null;
151
+ }
152
+ return {
153
+ key,
154
+ session: parsed.session,
155
+ timestamp: typeof parsed.timestamp === "number" ? parsed.timestamp : 0,
156
+ };
157
+ }
158
+ catch {
159
+ return null;
160
+ }
161
+ },
162
+ clear() {
163
+ safeClear();
164
+ },
165
+ };
166
+ }
@@ -1,56 +1,20 @@
1
1
  /**
2
- * Canonical stage vocabulary (M6 of the Coherent Options Surface track).
2
+ * Stage vocabulary of the section player's `pie-stage-change` event.
3
3
  *
4
- * One monotonic stage list is the unified replacement for the previous
5
- * drift of `toolkit-ready`, `section-ready`, `section-controller-ready`,
6
- * `interaction-ready`, `readiness-change`, and `ready` — all of which
7
- * have been removed. Hosts ask the same question — "is this CE at
8
- * stage ≥ X?" — across every `<pie-section-player-*>` and
9
- * `<pie-assessment-toolkit>` element.
4
+ * A cohort (one section and attempt) moves through `composed`,
5
+ * `engine-ready`, `interactive` and `disposed`, in that order. The section
6
+ * runtime engine in `@pie-players/pie-assessment-toolkit` is the only emitter;
7
+ * it dispatches the event on the layout element. `pie-loading-complete`
8
+ * reports that every item loaded and is not a stage. `readiness.mode =
9
+ * "strict"` only delays `interactive`.
10
10
  *
11
- * The four-stage canonical list (`composed`, `engine-ready`, `interactive`,
12
- * `disposed`) was finalized after the M5/M6 cumulative review confirmed
13
- * zero internal or external consumers for the original `attached`,
14
- * `runtime-bound`, and `ui-rendered` stages. The retro keeps the same
15
- * shape for both the toolkit CE and the layout CEs — every CE iterates
16
- * through the same list so subscribers don't need to special-case shape.
17
- *
18
- * Locked decisions (see `m6_ready_vocab_canonical_followup.plan.md`):
19
- * - **A1** Stage machine model with one canonical event family.
20
- * - **B1** "All items loaded" is reclassified as `pie-loading-complete`,
21
- * not a stage. (The previous `ready` DOM event alias was removed in
22
- * the broad architecture review compat sweep.)
23
- * - **C1** `readiness.mode = "strict"` only delays the `interactive`
24
- * transition; every earlier stage fires identically across modes.
25
- * - **D1** One DOM event family `pie-stage-change` with the stage in the
26
- * payload, mirroring the M3 `framework-error` idiom.
27
- *
28
- * Failure semantics deliberately stay in the M3 framework-error contract —
29
- * `status: "failed"` records the position; the framework error records the
30
- * cause.
11
+ * `status` is `entered`, or `failed` for the stage a non-recoverable
12
+ * framework error stopped the chain at, with `skipped` for each later stage
13
+ * up to `interactive`. The `framework-error` event carries the cause.
31
14
  */
32
15
  export declare const STAGES: readonly ["composed", "engine-ready", "interactive", "disposed"];
33
16
  export type Stage = (typeof STAGES)[number];
34
17
  export type StageStatus = "entered" | "skipped" | "failed";
35
- /**
36
- * Subset of the source CE shape used by stage subscribers. Both shapes
37
- * apply every canonical stage post-retro — the distinction stays in the
38
- * type so future shape-specific stages (e.g. layout-only `paginated`) can
39
- * be added without churn at the call sites.
40
- */
41
- export type StageSourceCe = "toolkit" | "layout";
42
- /**
43
- * Stages that apply to a given CE shape. Post-retro the toolkit and the
44
- * layout CEs share the same canonical list; `applicableStages` is kept as
45
- * an indirection so the tracker, telemetry, and tests don't have to be
46
- * rewritten when a future stage diverges between shapes.
47
- */
48
- export declare function applicableStages(_sourceCe: StageSourceCe): readonly Stage[];
49
- /**
50
- * Strict ordering index used by the tracker to detect monotonic
51
- * violations. `disposed` is last; `composed` is first.
52
- */
53
- export declare function stageOrdinal(stage: Stage): number;
54
18
  export type StageChangeDetail = {
55
19
  stage: Stage;
56
20
  status: StageStatus;
@@ -68,7 +32,6 @@ export type LoadingCompleteDetail = {
68
32
  sectionId: string;
69
33
  attemptId?: string;
70
34
  itemCount: number;
71
- loadedCount: number;
72
35
  timestamp: string;
73
36
  sourceCe: string;
74
37
  };
@@ -1,33 +1,16 @@
1
1
  /**
2
- * Canonical stage vocabulary (M6 of the Coherent Options Surface track).
2
+ * Stage vocabulary of the section player's `pie-stage-change` event.
3
3
  *
4
- * One monotonic stage list is the unified replacement for the previous
5
- * drift of `toolkit-ready`, `section-ready`, `section-controller-ready`,
6
- * `interaction-ready`, `readiness-change`, and `ready` — all of which
7
- * have been removed. Hosts ask the same question — "is this CE at
8
- * stage ≥ X?" — across every `<pie-section-player-*>` and
9
- * `<pie-assessment-toolkit>` element.
4
+ * A cohort (one section and attempt) moves through `composed`,
5
+ * `engine-ready`, `interactive` and `disposed`, in that order. The section
6
+ * runtime engine in `@pie-players/pie-assessment-toolkit` is the only emitter;
7
+ * it dispatches the event on the layout element. `pie-loading-complete`
8
+ * reports that every item loaded and is not a stage. `readiness.mode =
9
+ * "strict"` only delays `interactive`.
10
10
  *
11
- * The four-stage canonical list (`composed`, `engine-ready`, `interactive`,
12
- * `disposed`) was finalized after the M5/M6 cumulative review confirmed
13
- * zero internal or external consumers for the original `attached`,
14
- * `runtime-bound`, and `ui-rendered` stages. The retro keeps the same
15
- * shape for both the toolkit CE and the layout CEs — every CE iterates
16
- * through the same list so subscribers don't need to special-case shape.
17
- *
18
- * Locked decisions (see `m6_ready_vocab_canonical_followup.plan.md`):
19
- * - **A1** Stage machine model with one canonical event family.
20
- * - **B1** "All items loaded" is reclassified as `pie-loading-complete`,
21
- * not a stage. (The previous `ready` DOM event alias was removed in
22
- * the broad architecture review compat sweep.)
23
- * - **C1** `readiness.mode = "strict"` only delays the `interactive`
24
- * transition; every earlier stage fires identically across modes.
25
- * - **D1** One DOM event family `pie-stage-change` with the stage in the
26
- * payload, mirroring the M3 `framework-error` idiom.
27
- *
28
- * Failure semantics deliberately stay in the M3 framework-error contract —
29
- * `status: "failed"` records the position; the framework error records the
30
- * cause.
11
+ * `status` is `entered`, or `failed` for the stage a non-recoverable
12
+ * framework error stopped the chain at, with `skipped` for each later stage
13
+ * up to `interactive`. The `framework-error` event carries the cause.
31
14
  */
32
15
  export const STAGES = Object.freeze([
33
16
  "composed",
@@ -35,23 +18,3 @@ export const STAGES = Object.freeze([
35
18
  "interactive",
36
19
  "disposed",
37
20
  ]);
38
- /**
39
- * Stages that apply to a given CE shape. Post-retro the toolkit and the
40
- * layout CEs share the same canonical list; `applicableStages` is kept as
41
- * an indirection so the tracker, telemetry, and tests don't have to be
42
- * rewritten when a future stage diverges between shapes.
43
- */
44
- export function applicableStages(_sourceCe) {
45
- return STAGES;
46
- }
47
- /**
48
- * Strict ordering index used by the tracker to detect monotonic
49
- * violations. `disposed` is last; `composed` is first.
50
- */
51
- export function stageOrdinal(stage) {
52
- const idx = STAGES.indexOf(stage);
53
- if (idx === -1) {
54
- throw new Error(`Unknown stage \`${stage}\``);
55
- }
56
- return idx;
57
- }
@@ -15,3 +15,4 @@ export declare const toViewTag: (baseTag: string, view: PieViewMode | string, cu
15
15
  * Build a safe print tag by appending a deterministic, positive hash suffix.
16
16
  */
17
17
  export declare const toPrintHashedTag: (baseTag: string, hashSource: string, hashFn: (value: string) => number) => string;
18
+ export { parseVersionedTagName } from "./versioned-tag.js";
@@ -59,3 +59,4 @@ export const toPrintHashedTag = (baseTag, hashSource, hashFn) => {
59
59
  const hash = Math.abs(hashFn(hashSource));
60
60
  return `${baseTag}-print-${hash}`;
61
61
  };
62
+ export { parseVersionedTagName } from "./versioned-tag.js";
@@ -5,10 +5,6 @@
5
5
  * This module has NO runtime dependencies on other PIE modules.
6
6
  */
7
7
  import type { ConfigEntity, Env, PieController, PieModel } from "../types/index.js";
8
- /**
9
- * Player modes
10
- */
11
- export type PlayerMode = "gather" | "view" | "evaluate" | "author";
12
8
  /**
13
9
  * PIE custom element interface
14
10
  */
@@ -52,7 +48,8 @@ export interface AssetHandler {
52
48
  export declare enum BundleType {
53
49
  player = "player.js",// Elements only (no controllers)
54
50
  clientPlayer = "client-player.js",// Elements + controllers
55
- editor = "editor.js"
51
+ editor = "editor.js",// Editor UI
52
+ esm = "esm"
56
53
  }
57
54
  /**
58
55
  * Internal PIE registry entry status
@@ -93,6 +90,8 @@ export interface UpdatePieElementOptions {
93
90
  session: any[];
94
91
  env?: Env;
95
92
  invokeControllerForModel?: boolean;
93
+ /** The delivering player's bundle type, see `findPieController`. */
94
+ bundleType?: BundleType;
96
95
  eventListeners?: EventListeners;
97
96
  container?: Element | Document;
98
97
  /**
package/dist/pie/types.js CHANGED
@@ -12,6 +12,7 @@ export var BundleType;
12
12
  BundleType["player"] = "player.js";
13
13
  BundleType["clientPlayer"] = "client-player.js";
14
14
  BundleType["editor"] = "editor.js";
15
+ BundleType["esm"] = "esm";
15
16
  })(BundleType || (BundleType = {}));
16
17
  /**
17
18
  * Internal PIE registry entry status
@@ -4,7 +4,7 @@
4
4
  * Functions for updating PIE elements with new models, sessions, and env.
5
5
  */
6
6
  import type { ConfigEntity, Env } from "../types/index.js";
7
- import type { UpdatePieElementOptions } from "./types.js";
7
+ import type { BundleType, UpdatePieElementOptions } from "./types.js";
8
8
  /**
9
9
  * Update a PIE element by ref (direct Element reference)
10
10
  */
@@ -16,4 +16,4 @@ export declare const updatePieElement: (elName: string, opts: UpdatePieElementOp
16
16
  /**
17
17
  * Update all PIE elements in a config
18
18
  */
19
- export declare const updatePieElements: (config: ConfigEntity, session: any[], env: Env, container?: Element | Document, onElementSessionUpdate?: UpdatePieElementOptions["onElementSessionUpdate"]) => Promise<void>;
19
+ export declare const updatePieElements: (config: ConfigEntity, session: any[], env: Env, container?: Element | Document, onElementSessionUpdate?: UpdatePieElementOptions["onElementSessionUpdate"], bundleType?: BundleType) => Promise<void>;
@@ -4,7 +4,6 @@
4
4
  * Functions for updating PIE elements with new models, sessions, and env.
5
5
  */
6
6
  import { mergeObjectsIgnoringNullUndefined } from "../object/index.js";
7
- import { wrapModelRichContent } from "../security/wrap-model-rich-content.js";
8
7
  import { createPieLogger, isGlobalDebugEnabled } from "./logger.js";
9
8
  import { findPieController } from "./scoring.js";
10
9
  import { defaultPieElementOptions } from "./types.js";
@@ -55,11 +54,23 @@ const emitControllerError = (pieElement, detail) => {
55
54
  composed: true,
56
55
  }));
57
56
  };
57
+ /**
58
+ * Reports whether an element has left the document since its update started.
59
+ * A legacy element unmounts its React root when it is disconnected, and a
60
+ * write after that throws React error #409 in releases before PIE-703, or
61
+ * renders a tree nothing unmounts in later ones. The controller call is when a
62
+ * host can remove the player. An element that was never inserted still takes
63
+ * the update, so a caller can set one up before inserting it.
64
+ */
65
+ const trackRemoval = (element) => {
66
+ const wasConnected = element.isConnected;
67
+ return () => wasConnected && !element.isConnected;
68
+ };
58
69
  /**
59
70
  * Helper function to apply controller to element
60
71
  * Extracted to eliminate duplication and ensure consistent controller invocation
61
72
  */
62
- const applyControllerToElement = async (element, model, elementSession, controller, env, logPrefix, onElementSessionUpdate) => {
73
+ const applyControllerToElement = async (element, model, elementSession, controller, env, logPrefix, wasRemoved, onElementSessionUpdate) => {
63
74
  logger.debug(`${logPrefix} Using controller, env:`, env);
64
75
  logger.debug(`${logPrefix} Model before filter:`, {
65
76
  id: model.id,
@@ -93,15 +104,16 @@ const applyControllerToElement = async (element, model, elementSession, controll
93
104
  element: model.element,
94
105
  ...controllerResultObject,
95
106
  };
96
- const wrappedModel = wrapModelRichContent(filteredModel);
97
107
  logger.debug(`${logPrefix} ✅ Controller filtered model:`, {
98
- id: wrappedModel.id,
99
- element: wrappedModel.element,
100
- hasCorrectResponse: "correctResponse" in wrappedModel,
108
+ id: filteredModel.id,
109
+ element: filteredModel.element,
110
+ hasCorrectResponse: "correctResponse" in filteredModel,
101
111
  mode: env.mode,
102
112
  role: env.role,
103
113
  });
104
- element.model = wrappedModel;
114
+ if (wasRemoved())
115
+ return;
116
+ element.model = filteredModel;
105
117
  element.session = elementSession;
106
118
  }
107
119
  catch (err) {
@@ -110,7 +122,7 @@ const applyControllerToElement = async (element, model, elementSession, controll
110
122
  }
111
123
  };
112
124
  const resolveAndValidateUpdateOptions = (opts) => {
113
- const { config, session, env, eventListeners, invokeControllerForModel, onElementSessionUpdate, } = mergeObjectsIgnoringNullUndefined(defaultPieElementOptions, opts);
125
+ const { config, session, env, eventListeners, invokeControllerForModel, bundleType, onElementSessionUpdate, } = mergeObjectsIgnoringNullUndefined(defaultPieElementOptions, opts);
114
126
  if (!env) {
115
127
  throw new Error("env is required");
116
128
  }
@@ -126,11 +138,12 @@ const resolveAndValidateUpdateOptions = (opts) => {
126
138
  env,
127
139
  eventListeners,
128
140
  invokeControllerForModel,
141
+ bundleType,
129
142
  onElementSessionUpdate,
130
143
  };
131
144
  };
132
145
  const updateSinglePieElement = async (pieElement, controllerLookupTag, options, logContext) => {
133
- const { config, session, env, eventListeners, invokeControllerForModel, onElementSessionUpdate, } = options;
146
+ const { config, session, env, eventListeners, invokeControllerForModel, bundleType, onElementSessionUpdate, } = options;
134
147
  const model = config.models?.find((m) => m.id === pieElement.id);
135
148
  if (!model) {
136
149
  logger.error(`${logContext} Model not found for`, controllerLookupTag);
@@ -144,10 +157,10 @@ const updateSinglePieElement = async (pieElement, controllerLookupTag, options,
144
157
  });
145
158
  }
146
159
  if (env && invokeControllerForModel) {
147
- const controller = findPieController(controllerLookupTag);
160
+ const controller = findPieController(controllerLookupTag, bundleType);
148
161
  if (!controller) {
149
162
  logger.debug(`${logContext} ℹ️ No controller for ${controllerLookupTag}, using server-processed model`);
150
- pieElement.model = wrapModelRichContent(model);
163
+ pieElement.model = model;
151
164
  pieElement.session = elementSession;
152
165
  return;
153
166
  }
@@ -160,8 +173,9 @@ const updateSinglePieElement = async (pieElement, controllerLookupTag, options,
160
173
  // caller computes/emits the session signature; otherwise a late, async
161
174
  // shuffle write lands after the cycle that read the session and the order
162
175
  // never round-trips (PIE-631).
176
+ const wasRemoved = trackRemoval(pieElement);
163
177
  try {
164
- await applyControllerToElement(pieElement, model, elementSession, controller, env, `${logContext}(${controllerLookupTag}#${pieElement.id})`, onElementSessionUpdate);
178
+ await applyControllerToElement(pieElement, model, elementSession, controller, env, `${logContext}(${controllerLookupTag}#${pieElement.id})`, wasRemoved, onElementSessionUpdate);
165
179
  }
166
180
  catch (err) {
167
181
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -172,19 +186,22 @@ const updateSinglePieElement = async (pieElement, controllerLookupTag, options,
172
186
  ? "PIE_CONTROLLER_CONTRACT_ERROR"
173
187
  : "PIE_CONTROLLER_RUNTIME_ERROR",
174
188
  message: `${controllerLookupTag} controller failed while applying model for ${pieElement.id}. ${errorMessage}`,
189
+ recoverable: true,
175
190
  elementName: controllerLookupTag,
176
191
  elementId: pieElement.id,
177
192
  controllerShape: describeControllerShape(controller),
178
193
  cause: errorMessage,
179
194
  });
180
195
  // Fall back to raw model on controller error
181
- pieElement.model = wrapModelRichContent(model);
196
+ if (wasRemoved())
197
+ return;
198
+ pieElement.model = model;
182
199
  pieElement.session = elementSession;
183
200
  }
184
201
  }
185
202
  else {
186
203
  logger.debug(`${logContext} Direct model assignment for ${controllerLookupTag}#${pieElement.id} (no controller invocation requested)`);
187
- pieElement.model = wrapModelRichContent(model);
204
+ pieElement.model = model;
188
205
  pieElement.session = elementSession;
189
206
  }
190
207
  };
@@ -215,7 +232,7 @@ export const updatePieElement = (elName, opts) => {
215
232
  /**
216
233
  * Update all PIE elements in a config
217
234
  */
218
- export const updatePieElements = (config, session, env, container, onElementSessionUpdate) => {
235
+ export const updatePieElements = (config, session, env, container, onElementSessionUpdate, bundleType) => {
219
236
  logger.debug("[updatePieElements] Updating all elements with env:", env);
220
237
  return Promise.all(Object.entries(config.elements).map(([elName, _pkg]) => updatePieElement(elName, {
221
238
  config,
@@ -223,5 +240,6 @@ export const updatePieElements = (config, session, env, container, onElementSess
223
240
  env,
224
241
  container,
225
242
  onElementSessionUpdate,
243
+ bundleType,
226
244
  }))).then(() => undefined);
227
245
  };
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Split a runtime tag into its base name and the encoded version that follows
3
+ * `--version-`, when it has one.
4
+ */
1
5
  export declare function parseVersionedTagName(tagName: string): {
2
6
  baseName: string;
3
7
  existingEncodedVersion?: string;
@@ -6,6 +6,10 @@ const encodeVersionForTag = (version) => version
6
6
  .replace(/[.+]/g, "-")
7
7
  .replace(/[^0-9A-Za-z-]/g, "-")
8
8
  .replace(/-{2,}/g, "-");
9
+ /**
10
+ * Split a runtime tag into its base name and the encoded version that follows
11
+ * `--version-`, when it has one.
12
+ */
9
13
  export function parseVersionedTagName(tagName) {
10
14
  const versionMatch = tagName.match(new RegExp(`${VERSION_DELIMITER}(${TAG_VERSION_PATTERN})$`));
11
15
  if (!versionMatch || versionMatch.index === undefined) {
@@ -1,7 +1,6 @@
1
- export { buildAuthoringAllowList, createDefaultItemMarkupSanitizer, resetPurifierForTesting, sanitizeItemMarkup, type ItemMarkupSanitizer, type SanitizeItemMarkupOptions, } from "./sanitize-item-markup.js";
1
+ export { buildAuthoringAllowList, createDefaultItemMarkupSanitizer, sanitizeItemMarkup, type ItemMarkupSanitizer, type SanitizeItemMarkupOptions, } from "./sanitize-item-markup.js";
2
2
  export { parseAllowedStyleOrigins, validateExternalStyleUrl, type StyleUrlValidationError, type StyleUrlValidationOk, type StyleUrlValidationOptions, type StyleUrlValidationResult, } from "./validate-style-url.js";
3
- export { resetSvgSanitizerForTesting, sanitizeSvgIcon, } from "./sanitize-svg-icon.js";
3
+ export { sanitizeSvgIcon } from "./sanitize-svg-icon.js";
4
4
  export { sanitizeStyleAttribute } from "./sanitize-style-attribute.js";
5
5
  export { isOverwideImageWrapMutation, wrapOverwideImages, wrapOverwideImagesInElement, } from "./wrap-overwide-images.js";
6
- export { wrapModelRichContent } from "./wrap-model-rich-content.js";
7
6
  export { isOverwideTableWrapMutation, wrapOverwideTables, wrapOverwideTablesInElement, } from "./wrap-overwide-tables.js";
@@ -1,7 +1,6 @@
1
- export { buildAuthoringAllowList, createDefaultItemMarkupSanitizer, resetPurifierForTesting, sanitizeItemMarkup, } from "./sanitize-item-markup.js";
1
+ export { buildAuthoringAllowList, createDefaultItemMarkupSanitizer, sanitizeItemMarkup, } from "./sanitize-item-markup.js";
2
2
  export { parseAllowedStyleOrigins, validateExternalStyleUrl, } from "./validate-style-url.js";
3
- export { resetSvgSanitizerForTesting, sanitizeSvgIcon, } from "./sanitize-svg-icon.js";
3
+ export { sanitizeSvgIcon } from "./sanitize-svg-icon.js";
4
4
  export { sanitizeStyleAttribute } from "./sanitize-style-attribute.js";
5
5
  export { isOverwideImageWrapMutation, wrapOverwideImages, wrapOverwideImagesInElement, } from "./wrap-overwide-images.js";
6
- export { wrapModelRichContent } from "./wrap-model-rich-content.js";
7
6
  export { isOverwideTableWrapMutation, wrapOverwideTables, wrapOverwideTablesInElement, } from "./wrap-overwide-tables.js";
@@ -35,6 +35,10 @@ export interface SanitizeItemMarkupOptions {
35
35
  * `form`, `meta`, `link`).
36
36
  * - Preserves PIE custom elements (`pie-*`) and any extra tags listed in
37
37
  * `allowedCustomElements`.
38
+ * - Marks elements that carry an authored color, as `markAuthoredColors`
39
+ * in `@pie-element/shared-utils` describes, so a color scheme can override
40
+ * it. A host that opts out with `trust-markup` or supplies its own
41
+ * sanitizer gets no markers.
38
42
  * - During SSR (no `window`) returns an empty string so untrusted markup
39
43
  * never reaches the prerender output; the live renderer will re-run the
40
44
  * sanitizer on hydrate.
@@ -52,5 +56,3 @@ export declare function createDefaultItemMarkupSanitizer(options?: SanitizeItemM
52
56
  * keeps the rewritten `-config` tags instead of stripping them.
53
57
  */
54
58
  export declare function buildAuthoringAllowList(elementTagNames: Iterable<string>): string[];
55
- /** Reset the memoised DOMPurify instance. Only intended for tests. */
56
- export declare function resetPurifierForTesting(): void;