@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.
- package/README.md +30 -2
- package/dist/components/vendor/nds/README.md +7 -5
- package/dist/config/profile.d.ts +0 -2
- package/dist/config/profile.js +2 -8
- package/dist/i18n/index.d.ts +2 -3
- package/dist/i18n/index.js +2 -3
- package/dist/i18n/messages/en-US.d.ts +11 -24
- package/dist/i18n/messages/en-US.js +13 -26
- package/dist/i18n/messages/nl-NL.d.ts +7 -24
- package/dist/i18n/messages/nl-NL.js +9 -26
- package/dist/i18n/provider.d.ts +4 -5
- package/dist/i18n/provider.js +4 -5
- package/dist/i18n/types.d.ts +4 -5
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/instrumentation/index.d.ts +5 -4
- package/dist/instrumentation/index.js +5 -4
- package/dist/instrumentation/new-relic-agent.d.ts +20 -0
- package/dist/instrumentation/new-relic-agent.js +24 -0
- package/dist/instrumentation/providers/BaseInstrumentationProvider.d.ts +5 -4
- package/dist/instrumentation/providers/NewRelicInstrumentationProvider.d.ts +7 -7
- package/dist/instrumentation/providers/NewRelicInstrumentationProvider.js +21 -32
- package/dist/instrumentation/types.d.ts +7 -3
- package/dist/loader-config.d.ts +16 -6
- package/dist/loaders/controller-loading.d.ts +14 -0
- package/dist/loaders/controller-loading.js +12 -0
- package/dist/loaders/defaults.d.ts +10 -0
- package/dist/loaders/defaults.js +10 -0
- package/dist/loaders/element-loader.d.ts +20 -8
- package/dist/loaders/element-loader.js +102 -36
- package/dist/loaders/element-package-policy.d.ts +2 -0
- package/dist/loaders/element-package-policy.js +2 -1
- package/dist/loaders/esm-adapter.d.ts +29 -0
- package/dist/loaders/esm-adapter.js +635 -102
- package/dist/loaders/iife-adapter.d.ts +0 -5
- package/dist/loaders/iife-adapter.js +3 -9
- package/dist/loaders/index.d.ts +12 -3
- package/dist/loaders/index.js +9 -3
- package/dist/loaders/mathjax-events.d.ts +22 -0
- package/dist/loaders/mathjax-events.js +65 -0
- package/dist/loaders/module-shim.d.ts +12 -0
- package/dist/loaders/module-shim.js +12 -0
- package/dist/loaders/preloaded-alignment.d.ts +24 -0
- package/dist/loaders/preloaded-alignment.js +57 -0
- package/dist/loaders/preloaded-authored-tags.d.ts +26 -0
- package/dist/loaders/preloaded-authored-tags.js +69 -0
- package/dist/loaders/preloaded-registration.d.ts +72 -0
- package/dist/loaders/preloaded-registration.js +141 -0
- package/dist/media/index.d.ts +69 -0
- package/dist/media/index.js +194 -0
- package/dist/pie/authoring.d.ts +1 -1
- package/dist/pie/authoring.js +25 -18
- package/dist/pie/element-announcements.d.ts +39 -0
- package/dist/pie/element-announcements.js +67 -0
- package/dist/pie/element-observer.d.ts +3 -1
- package/dist/pie/element-observer.js +1 -0
- package/dist/pie/elements-rendered.d.ts +25 -0
- package/dist/pie/elements-rendered.js +96 -0
- package/dist/pie/index.d.ts +8 -10
- package/dist/pie/index.js +9 -10
- package/dist/pie/initialization.js +13 -22
- package/dist/pie/initialize-element.d.ts +3 -0
- package/dist/pie/initialize-element.js +3 -4
- package/dist/pie/instrumentation-event-bridge.js +23 -1
- package/dist/pie/instrumentation-event-map.js +11 -25
- package/dist/pie/instrumentation-provider-resolution.d.ts +2 -1
- package/dist/pie/instrumentation-provider-resolution.js +0 -1
- package/dist/pie/item-controller.js +4 -0
- package/dist/pie/item-session-contract.d.ts +8 -0
- package/dist/pie/item-session-contract.js +30 -9
- package/dist/pie/logger.d.ts +6 -0
- package/dist/pie/logger.js +10 -0
- package/dist/pie/markup-math.d.ts +20 -0
- package/dist/pie/markup-math.js +118 -0
- package/dist/pie/math-assets.d.ts +48 -0
- package/dist/pie/math-assets.js +81 -0
- package/dist/pie/math-rendering.d.ts +10 -21
- package/dist/pie/math-rendering.js +50 -35
- package/dist/pie/registry.d.ts +13 -1
- package/dist/pie/registry.js +39 -1
- package/dist/pie/resource-monitor.d.ts +6 -33
- package/dist/pie/resource-monitor.js +14 -213
- package/dist/pie/scoring.d.ts +12 -8
- package/dist/pie/scoring.js +21 -24
- package/dist/pie/session-commit.d.ts +16 -0
- package/dist/pie/session-commit.js +34 -6
- package/dist/pie/session-snapshot.d.ts +80 -0
- package/dist/pie/session-snapshot.js +166 -0
- package/dist/pie/stages.d.ts +10 -47
- package/dist/pie/stages.js +10 -47
- package/dist/pie/tag-names.d.ts +1 -0
- package/dist/pie/tag-names.js +1 -0
- package/dist/pie/types.d.ts +4 -5
- package/dist/pie/types.js +1 -0
- package/dist/pie/updates.d.ts +2 -2
- package/dist/pie/updates.js +33 -15
- package/dist/pie/versioned-tag.d.ts +4 -0
- package/dist/pie/versioned-tag.js +4 -0
- package/dist/security/index.d.ts +2 -3
- package/dist/security/index.js +2 -3
- package/dist/security/sanitize-item-markup.d.ts +4 -2
- package/dist/security/sanitize-item-markup.js +48 -6
- package/dist/security/sanitize-svg-icon.d.ts +0 -2
- package/dist/security/sanitize-svg-icon.js +0 -5
- package/dist/security/wrap-overwide-images.js +19 -0
- package/dist/security/wrap-overwide.d.ts +13 -0
- package/dist/security/wrap-overwide.js +63 -3
- package/dist/timed-media/policy.js +5 -16
- package/dist/types/index.d.ts +67 -423
- package/dist/types/index.js +0 -68
- package/dist/ui/attribute-coercion.d.ts +12 -0
- package/dist/ui/attribute-coercion.js +33 -0
- package/dist/ui/content-styles.d.ts +18 -16
- package/dist/ui/content-styles.js +67 -36
- package/dist/ui/overlay-containment.d.ts +31 -0
- package/dist/ui/overlay-containment.js +63 -0
- package/dist/ui/overlay-placement.d.ts +110 -0
- package/dist/ui/overlay-placement.js +222 -0
- package/dist/ui/pointer-gesture.d.ts +32 -0
- package/dist/ui/pointer-gesture.js +56 -0
- package/dist/ui/pointer-rotate.d.ts +29 -0
- package/dist/ui/pointer-rotate.js +39 -0
- package/package.json +23 -18
- package/dist/pie/component-context.d.ts +0 -21
- package/dist/pie/component-context.js +0 -29
- package/dist/pie/player-initializer.d.ts +0 -54
- package/dist/pie/player-initializer.js +0 -122
- package/dist/pie/stage-tracker.d.ts +0 -50
- package/dist/pie/stage-tracker.js +0 -105
- package/dist/security/wrap-model-rich-content.d.ts +0 -1
- package/dist/security/wrap-model-rich-content.js +0 -40
- package/dist/server/npm-auth-env.d.ts +0 -13
- package/dist/server/npm-auth-env.js +0 -64
- package/dist/server/npm-registry.d.ts +0 -7
- package/dist/server/npm-registry.js +0 -59
- package/dist/ui/debug-panel-persistence.d.ts +0 -17
- package/dist/ui/debug-panel-persistence.js +0 -37
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When the PIE elements of a player have rendered.
|
|
3
|
+
*
|
|
4
|
+
* An element renders after its model and session are assigned, in a task of
|
|
5
|
+
* its own: legacy React elements debounce the render and commit through the
|
|
6
|
+
* React scheduler, so the commit lands tens of milliseconds after the
|
|
7
|
+
* assignment. No event marks it in both element generations — `model-set` is
|
|
8
|
+
* dispatched by about half of them, some before rendering — so this reads the
|
|
9
|
+
* DOM.
|
|
10
|
+
*
|
|
11
|
+
* An element has rendered once it holds content, and so has every custom
|
|
12
|
+
* element defined below it: an `ebsr` paints its parts, which then render
|
|
13
|
+
* themselves. Some elements render nothing, a rubric for a student among them,
|
|
14
|
+
* so an element still empty when the subtree has had no mutation for
|
|
15
|
+
* `quietMs` counts as rendered. A render slower than that window, with no
|
|
16
|
+
* mutation before it, is missed rather than waited for.
|
|
17
|
+
*/
|
|
18
|
+
/** Longer than the 50ms render debounce legacy elements use, plus a scheduler task. */
|
|
19
|
+
export const ELEMENT_RENDER_QUIET_MS = 200;
|
|
20
|
+
function hasContent(element) {
|
|
21
|
+
if (element.firstElementChild || element.shadowRoot?.firstChild)
|
|
22
|
+
return true;
|
|
23
|
+
return Boolean(element.textContent?.trim());
|
|
24
|
+
}
|
|
25
|
+
function isDefinedCustomElement(element) {
|
|
26
|
+
return (element.localName.includes("-") &&
|
|
27
|
+
customElements.get(element.localName) !== undefined);
|
|
28
|
+
}
|
|
29
|
+
function hasRendered(element) {
|
|
30
|
+
if (!element.isConnected)
|
|
31
|
+
return true;
|
|
32
|
+
if (!hasContent(element))
|
|
33
|
+
return false;
|
|
34
|
+
for (const nested of element.querySelectorAll("*")) {
|
|
35
|
+
if (isDefinedCustomElement(nested) && !hasContent(nested))
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
/** The elements of `configs` in `root`, found as `updatePieElements` finds them. */
|
|
41
|
+
function renderedElements(root, configs) {
|
|
42
|
+
const elements = [];
|
|
43
|
+
for (const config of configs) {
|
|
44
|
+
const ids = new Set((config.models ?? []).map((model) => model?.id));
|
|
45
|
+
for (const tag of Object.keys(config.elements ?? {})) {
|
|
46
|
+
// An undefined tag renders nothing until its bundle loads.
|
|
47
|
+
if (!customElements.get(tag))
|
|
48
|
+
continue;
|
|
49
|
+
for (const element of root.querySelectorAll(tag)) {
|
|
50
|
+
if (ids.has(element.id))
|
|
51
|
+
elements.push(element);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return elements;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolves once every element of `configs` in `root` has rendered, or when
|
|
59
|
+
* `signal` aborts. Never rejects.
|
|
60
|
+
*/
|
|
61
|
+
export function elementsRendered(root, configs, signal, quietMs = ELEMENT_RENDER_QUIET_MS) {
|
|
62
|
+
const elements = renderedElements(root, configs);
|
|
63
|
+
const allRendered = () => elements.every(hasRendered);
|
|
64
|
+
if (signal.aborted ||
|
|
65
|
+
allRendered() ||
|
|
66
|
+
typeof MutationObserver === "undefined") {
|
|
67
|
+
return Promise.resolve();
|
|
68
|
+
}
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
let quiet;
|
|
71
|
+
const observer = new MutationObserver(() => {
|
|
72
|
+
if (allRendered()) {
|
|
73
|
+
done();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
armQuiet();
|
|
77
|
+
});
|
|
78
|
+
function armQuiet() {
|
|
79
|
+
clearTimeout(quiet);
|
|
80
|
+
quiet = setTimeout(done, quietMs);
|
|
81
|
+
}
|
|
82
|
+
function done() {
|
|
83
|
+
clearTimeout(quiet);
|
|
84
|
+
observer.disconnect();
|
|
85
|
+
signal.removeEventListener("abort", done);
|
|
86
|
+
resolve();
|
|
87
|
+
}
|
|
88
|
+
observer.observe(root, {
|
|
89
|
+
childList: true,
|
|
90
|
+
subtree: true,
|
|
91
|
+
characterData: true,
|
|
92
|
+
});
|
|
93
|
+
signal.addEventListener("abort", done);
|
|
94
|
+
armQuiet();
|
|
95
|
+
});
|
|
96
|
+
}
|
package/dist/pie/index.d.ts
CHANGED
|
@@ -14,28 +14,26 @@ export { observePieElements, pieElementContextsWithin, } from "./element-observe
|
|
|
14
14
|
export type { PieElementContext, PieElementContextSource, } from "./element-observer.js";
|
|
15
15
|
export { createAuthoringAssetEventManager, initializeAuthoringConfigures, resolveConfigureConfiguration, validateAuthoringModels, } from "./authoring.js";
|
|
16
16
|
export type { AuthoringMediaHandlers, AuthoringValidationResult, InitializedConfigureModel, } from "./authoring.js";
|
|
17
|
-
export { initializeMathRendering,
|
|
17
|
+
export { initializeMathRendering, setMathRenderer, } from "./math-rendering.js";
|
|
18
18
|
export { attachInstrumentationEventBridge } from "./instrumentation-event-bridge.js";
|
|
19
19
|
export type { AttachInstrumentationEventBridgeArgs } from "./instrumentation-event-bridge.js";
|
|
20
20
|
export { resolveInstrumentationProvider } from "./instrumentation-provider-resolution.js";
|
|
21
|
-
export { STAGES
|
|
22
|
-
export type { LoadingCompleteDetail, Stage, StageChangeDetail,
|
|
23
|
-
export { createStageTracker } from "./stage-tracker.js";
|
|
24
|
-
export type { CreateStageTrackerOptions, StageTracker, } from "./stage-tracker.js";
|
|
21
|
+
export { STAGES } from "./stages.js";
|
|
22
|
+
export type { LoadingCompleteDetail, Stage, StageChangeDetail, StageStatus, } from "./stages.js";
|
|
25
23
|
export { ASSESSMENT_INSTRUMENTATION_EVENT_MAP, ITEM_INSTRUMENTATION_EVENT_MAP, SECTION_INSTRUMENTATION_EVENT_MAP, TOOLKIT_INSTRUMENTATION_EVENT_MAP, } from "./instrumentation-event-map.js";
|
|
26
24
|
export type { InstrumentationEventMapping } from "./instrumentation-event-map.js";
|
|
27
25
|
export type { ItemControllerOptions } from "./item-controller.js";
|
|
28
26
|
export { ItemController, normalizeItemSessionContainer, } from "./item-controller.js";
|
|
29
27
|
export type { ItemSessionContainer as CanonicalItemSessionContainer, ItemSessionUpdateIntent, NormalizedItemSessionChange, } from "./item-session-contract.js";
|
|
30
|
-
export { ensureHostSessionEntries, hasLearnerResponse, hasResponseValue, normalizeItemSessionChange, projectSessionIntoHostContainer, } from "./item-session-contract.js";
|
|
28
|
+
export { ensureHostSessionEntries, hasLearnerResponse, hasResponseField, hasResponseValue, normalizeItemSessionChange, projectSessionIntoHostContainer, } from "./item-session-contract.js";
|
|
31
29
|
export type { ItemSessionContainer, ItemSessionStorageStrategy, } from "./item-controller-storage.js";
|
|
32
30
|
export { MemoryItemSessionStorage, SessionStorageItemSessionStorage, } from "./item-controller-storage.js";
|
|
33
|
-
export { SESSION_COMMIT_METHOD, bindPageLifecycleCommit, commitPendingSessions, noteSessionBaseline, noteSessionObserved, } from "./session-commit.js";
|
|
31
|
+
export { SESSION_COMMIT_METHOD, bindPageLifecycleCommit, commitPendingSessions, flushPendingSessionNotifications, noteSessionBaseline, noteSessionObserved, } from "./session-commit.js";
|
|
34
32
|
export type { BindPageLifecycleCommitOptions, CommitPendingSessionsOptions, CommitPendingSessionsResult, SessionCommitReason, } from "./session-commit.js";
|
|
33
|
+
export { createSessionSnapshot, isSessionSnapshotEnabled, isSnapshotIdentitySpecific, sessionSnapshotKey, sessionStorageSnapshotStore, } from "./session-snapshot.js";
|
|
34
|
+
export type { SessionSnapshot, SessionSnapshotConfig, SessionSnapshotIdentity, SessionSnapshotRecord, SessionSnapshotStore, } from "./session-snapshot.js";
|
|
35
35
|
export type { PieLogger } from "./logger.js";
|
|
36
|
-
export { createPieLogger, isGlobalDebugEnabled } from "./logger.js";
|
|
37
|
-
export type { ItemData, PiePlayerConfig, PiePlayerElements, } from "./player-initializer.js";
|
|
38
|
-
export { buildApiParams, buildEventListenersMap, ensurePiePlayerLoaded, extractPassageMarkup, fetchItemData, initializePiePlayer, } from "./player-initializer.js";
|
|
36
|
+
export { createPieLogger, isGlobalDebugEnabled, isTtsDebugEnabled, } from "./logger.js";
|
|
39
37
|
export { pieRegistry } from "./registry.js";
|
|
40
38
|
export { findPieController, scorePieItem } from "./scoring.js";
|
|
41
39
|
export type { Entry, EventListeners, EventListenersMap, LoadPieElementsOptions, PieElement, PieRegistry, UpdatePieElementOptions, } from "./types.js";
|
package/dist/pie/index.js
CHANGED
|
@@ -20,21 +20,20 @@ export { initializePiesFromLoadedBundle, loadBundleFromString, loadPieModule, lo
|
|
|
20
20
|
// owns.
|
|
21
21
|
export { observePieElements, pieElementContextsWithin, } from "./element-observer.js";
|
|
22
22
|
export { createAuthoringAssetEventManager, initializeAuthoringConfigures, resolveConfigureConfiguration, validateAuthoringModels, } from "./authoring.js";
|
|
23
|
-
export { initializeMathRendering,
|
|
23
|
+
export { initializeMathRendering, setMathRenderer, } from "./math-rendering.js";
|
|
24
24
|
export { attachInstrumentationEventBridge } from "./instrumentation-event-bridge.js";
|
|
25
25
|
export { resolveInstrumentationProvider } from "./instrumentation-provider-resolution.js";
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
// `pie-stage-change` event family stays coherent across CE shapes.
|
|
29
|
-
export { STAGES, applicableStages, stageOrdinal, } from "./stages.js";
|
|
30
|
-
export { createStageTracker } from "./stage-tracker.js";
|
|
26
|
+
// Stage vocabulary of the section player's `pie-stage-change` event.
|
|
27
|
+
export { STAGES } from "./stages.js";
|
|
31
28
|
export { ASSESSMENT_INSTRUMENTATION_EVENT_MAP, ITEM_INSTRUMENTATION_EVENT_MAP, SECTION_INSTRUMENTATION_EVENT_MAP, TOOLKIT_INSTRUMENTATION_EVENT_MAP, } from "./instrumentation-event-map.js";
|
|
32
29
|
export { ItemController, normalizeItemSessionContainer, } from "./item-controller.js";
|
|
33
|
-
export { ensureHostSessionEntries, hasLearnerResponse, hasResponseValue, normalizeItemSessionChange, projectSessionIntoHostContainer, } from "./item-session-contract.js";
|
|
30
|
+
export { ensureHostSessionEntries, hasLearnerResponse, hasResponseField, hasResponseValue, normalizeItemSessionChange, projectSessionIntoHostContainer, } from "./item-session-contract.js";
|
|
34
31
|
export { MemoryItemSessionStorage, SessionStorageItemSessionStorage, } from "./item-controller-storage.js";
|
|
35
|
-
export { SESSION_COMMIT_METHOD, bindPageLifecycleCommit, commitPendingSessions, noteSessionBaseline, noteSessionObserved, } from "./session-commit.js";
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
export { SESSION_COMMIT_METHOD, bindPageLifecycleCommit, commitPendingSessions, flushPendingSessionNotifications, noteSessionBaseline, noteSessionObserved, } from "./session-commit.js";
|
|
33
|
+
// Opt-in device-local snapshot of a committed session, offered back to the host
|
|
34
|
+
// after a crash rather than applied.
|
|
35
|
+
export { createSessionSnapshot, isSessionSnapshotEnabled, isSnapshotIdentitySpecific, sessionSnapshotKey, sessionStorageSnapshotStore, } from "./session-snapshot.js";
|
|
36
|
+
export { createPieLogger, isGlobalDebugEnabled, isTtsDebugEnabled, } from "./logger.js";
|
|
38
37
|
// Registry
|
|
39
38
|
export { pieRegistry } from "./registry.js";
|
|
40
39
|
// Scoring
|
|
@@ -11,7 +11,7 @@ import { editorPostFix } from "../types/index.js";
|
|
|
11
11
|
import { initializePieElement } from "./initialize-element.js";
|
|
12
12
|
import { createPieLogger, isGlobalDebugEnabled } from "./logger.js";
|
|
13
13
|
import { initializeMathRendering } from "./math-rendering.js";
|
|
14
|
-
import { pieRegistry } from "./registry.js";
|
|
14
|
+
import { pieRegistry, writeRegistryEntry } from "./registry.js";
|
|
15
15
|
import { defineCustomElementSafely } from "./custom-element-define.js";
|
|
16
16
|
import { validateCustomElementTag } from "./tag-names.js";
|
|
17
17
|
import { BundleType, isCustomElementConstructor, isPieAvailable, Status, } from "./types.js";
|
|
@@ -38,6 +38,7 @@ const updateRegisteredElement = (elementTagName, config, session, options, omitE
|
|
|
38
38
|
config,
|
|
39
39
|
session,
|
|
40
40
|
...(omitEnv ? {} : { env: options.env }),
|
|
41
|
+
bundleType: options.bundleType,
|
|
41
42
|
container: options.container,
|
|
42
43
|
...(options.eventListeners?.[elementTagName] && {
|
|
43
44
|
eventListeners: options.eventListeners[elementTagName],
|
|
@@ -119,30 +120,21 @@ const registerPieElementsFromBundle = (elementModule, config, session, registry,
|
|
|
119
120
|
: [],
|
|
120
121
|
bundleType: options.bundleType,
|
|
121
122
|
});
|
|
122
|
-
// Validate controller presence based on bundle type
|
|
123
|
-
if (!elementData.controller) {
|
|
124
|
-
if (options.bundleType === BundleType.clientPlayer) {
|
|
125
|
-
logger.error(`[registerPieElementsFromBundle] ❌ CRITICAL: No controller found for ${pkgStripped}!`);
|
|
126
|
-
logger.error(`[registerPieElementsFromBundle] Bundle type: ${options.bundleType} (controllers required)`);
|
|
127
|
-
throw new Error(`No controller found for ${pkgStripped}. client-player.js bundles MUST include controllers!`);
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
logger.debug(`[registerPieElementsFromBundle] ℹ️ No controller found for ${pkgStripped} - using server-processed models (player.js bundle)`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
123
|
{
|
|
134
|
-
// Register the element in our registry
|
|
124
|
+
// Register the element in our registry. A package without a
|
|
125
|
+
// controller, such as a legacy `@pie-element/protractor`, renders
|
|
126
|
+
// the model it is given under client-player.js too.
|
|
135
127
|
logger.debug(`[registerPieElementsFromBundle] Registering ${elName} in registry${elementData.controller
|
|
136
128
|
? " with controller"
|
|
137
|
-
: " (no controller
|
|
138
|
-
|
|
129
|
+
: " (no controller; its model is used as given)"}`);
|
|
130
|
+
writeRegistryEntry({
|
|
139
131
|
package: pkg,
|
|
140
132
|
status: Status.loading,
|
|
141
133
|
tagName: elementTagName,
|
|
142
134
|
controller: elementData.controller || null,
|
|
143
135
|
config: elementData.config,
|
|
144
136
|
bundleType: options.bundleType,
|
|
145
|
-
};
|
|
137
|
+
});
|
|
146
138
|
if (isCustomElementConstructor(elementData.Element)) {
|
|
147
139
|
defineCustomElementSafely(elementTagName, elementData.Element, `element tag in config.elements for ${String(pkg)}`);
|
|
148
140
|
// Initialize existing elements
|
|
@@ -155,13 +147,13 @@ const registerPieElementsFromBundle = (elementModule, config, session, registry,
|
|
|
155
147
|
session,
|
|
156
148
|
env: options.env,
|
|
157
149
|
eventListeners: options.eventListeners?.[elementTagName],
|
|
150
|
+
bundleType: options.bundleType,
|
|
158
151
|
});
|
|
159
152
|
});
|
|
160
|
-
|
|
161
|
-
registry[elementTagName] = {
|
|
153
|
+
writeRegistryEntry({
|
|
162
154
|
...registry[elementTagName],
|
|
163
155
|
status: Status.loaded,
|
|
164
|
-
};
|
|
156
|
+
});
|
|
165
157
|
promises.push(customElements.whenDefined(elementTagName).then(() => {
|
|
166
158
|
logger.debug("[registerPieElementsFromBundle] defined custom PIE element: %s", elementTagName);
|
|
167
159
|
}));
|
|
@@ -297,9 +289,8 @@ export const loadPieModule = async (config, session, opts = {}) => {
|
|
|
297
289
|
}
|
|
298
290
|
catch (error) {
|
|
299
291
|
// `registerPieElementsFromBundle` throws synchronously for a
|
|
300
|
-
// package missing from the bundle
|
|
301
|
-
//
|
|
302
|
-
// throw reaches the window instead of the caller.
|
|
292
|
+
// package missing from the bundle. Inside a DOM event handler
|
|
293
|
+
// that throw reaches the window instead of the caller.
|
|
303
294
|
fail(error instanceof Error ? error : new Error(String(error)));
|
|
304
295
|
}
|
|
305
296
|
});
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { ConfigEntity, Env } from "../types/index.js";
|
|
8
8
|
import type { EventListeners, PieElement } from "./types.js";
|
|
9
|
+
import { BundleType } from "./types.js";
|
|
9
10
|
/**
|
|
10
11
|
* Bind `element` to the model in `options.config` whose `id` matches it.
|
|
11
12
|
*
|
|
@@ -20,4 +21,6 @@ export declare const initializePieElement: (element: PieElement, options: {
|
|
|
20
21
|
session: any[];
|
|
21
22
|
env?: Env;
|
|
22
23
|
eventListeners?: EventListeners;
|
|
24
|
+
/** The delivering player's bundle type, see `findPieController`. */
|
|
25
|
+
bundleType?: BundleType;
|
|
23
26
|
}) => boolean;
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* Split out of `initialization.ts` so the late-arrival observer in
|
|
5
5
|
* `element-observer.ts` can reuse it without importing the bundle loaders.
|
|
6
6
|
*/
|
|
7
|
-
import { wrapModelRichContent } from "../security/wrap-model-rich-content.js";
|
|
8
7
|
import { createPieLogger, isGlobalDebugEnabled } from "./logger.js";
|
|
9
8
|
import { pieRegistry } from "./registry.js";
|
|
10
9
|
import { findPieController } from "./scoring.js";
|
|
@@ -21,7 +20,7 @@ const logger = createPieLogger("pie-initialize-element", () => isGlobalDebugEnab
|
|
|
21
20
|
* separately — uses that to stop at the config the element belongs to.
|
|
22
21
|
*/
|
|
23
22
|
export const initializePieElement = (element, options) => {
|
|
24
|
-
const { config, session, env, eventListeners } = options;
|
|
23
|
+
const { config, session, env, eventListeners, bundleType } = options;
|
|
25
24
|
if (element.__pieInitialized) {
|
|
26
25
|
return true;
|
|
27
26
|
}
|
|
@@ -45,7 +44,7 @@ export const initializePieElement = (element, options) => {
|
|
|
45
44
|
element.__pieInitialized = true;
|
|
46
45
|
logger.debug(`[initializePieElement] Session set for ${tagName}#${element.id}:`, elementSession);
|
|
47
46
|
// Set model - use controller if available (client-player.js), or use server-processed model (player.js)
|
|
48
|
-
const controller = findPieController(tagName);
|
|
47
|
+
const controller = findPieController(tagName, bundleType);
|
|
49
48
|
if (!env) {
|
|
50
49
|
logger.error(`[initializePieElement] ❌ FATAL: No env provided for ${tagName}`);
|
|
51
50
|
throw new Error(`No env provided for ${tagName}. PIE elements require an env object with mode and role.`);
|
|
@@ -61,7 +60,7 @@ export const initializePieElement = (element, options) => {
|
|
|
61
60
|
role: env.role,
|
|
62
61
|
});
|
|
63
62
|
// Set model directly - server already processed it
|
|
64
|
-
element.model =
|
|
63
|
+
element.model = model;
|
|
65
64
|
}
|
|
66
65
|
else {
|
|
67
66
|
// Controller available - run client-side processing (client-player.js bundle)
|
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
import { isInstrumentationProvider } from "../instrumentation/provider-guards.js";
|
|
2
2
|
import { createPieLogger, isGlobalDebugEnabled } from "./logger.js";
|
|
3
3
|
const logger = createPieLogger("instrumentation-event-bridge", () => isGlobalDebugEnabled());
|
|
4
|
+
/**
|
|
5
|
+
* Telemetry attributes are data. A detail's live objects (a coordinator on
|
|
6
|
+
* `toolkit-ready`, an `Error` cause, a function) stay on the event for its
|
|
7
|
+
* listeners and are left out of the attributes; primitives, arrays and plain
|
|
8
|
+
* objects pass.
|
|
9
|
+
*/
|
|
10
|
+
function isAttributeValue(value) {
|
|
11
|
+
if (value === null)
|
|
12
|
+
return true;
|
|
13
|
+
const type = typeof value;
|
|
14
|
+
if (type === "function" || type === "symbol")
|
|
15
|
+
return false;
|
|
16
|
+
if (type !== "object" || Array.isArray(value))
|
|
17
|
+
return true;
|
|
18
|
+
const prototype = Object.getPrototypeOf(value);
|
|
19
|
+
return prototype === Object.prototype || prototype === null;
|
|
20
|
+
}
|
|
4
21
|
function normalizeEventDetail(detail) {
|
|
5
22
|
if (detail && typeof detail === "object") {
|
|
6
|
-
|
|
23
|
+
const attributes = {};
|
|
24
|
+
for (const [key, value] of Object.entries(detail)) {
|
|
25
|
+
if (isAttributeValue(value))
|
|
26
|
+
attributes[key] = value;
|
|
27
|
+
}
|
|
28
|
+
return attributes;
|
|
7
29
|
}
|
|
8
30
|
if (typeof detail === "undefined") {
|
|
9
31
|
return {};
|
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
export const TOOLKIT_INSTRUMENTATION_EVENT_MAP = [
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// `attached`, `runtime-bound`, and `ui-rendered` stages because they
|
|
6
|
-
// had zero internal or external consumers. Hosts listen for the
|
|
7
|
-
// DOM-prefixed name directly (`pie-stage-change`); the instrumentation
|
|
8
|
-
// bridge forwards the same name to telemetry.
|
|
9
|
-
{
|
|
10
|
-
sourceEventName: "pie-stage-change",
|
|
11
|
-
instrumentationEventName: "pie-toolkit-stage-change",
|
|
12
|
-
},
|
|
2
|
+
// The toolkit emits no stage events: the section player's runtime
|
|
3
|
+
// engine dispatches `pie-stage-change` on the layout element, above the
|
|
4
|
+
// toolkit, and the section map tracks it there.
|
|
13
5
|
{
|
|
14
6
|
sourceEventName: "runtime-owned",
|
|
15
7
|
instrumentationEventName: "pie-toolkit-runtime-owned",
|
|
@@ -18,6 +10,10 @@ export const TOOLKIT_INSTRUMENTATION_EVENT_MAP = [
|
|
|
18
10
|
sourceEventName: "runtime-inherited",
|
|
19
11
|
instrumentationEventName: "pie-toolkit-runtime-inherited",
|
|
20
12
|
},
|
|
13
|
+
{
|
|
14
|
+
sourceEventName: "runtime-ready",
|
|
15
|
+
instrumentationEventName: "pie-toolkit-runtime-ready",
|
|
16
|
+
},
|
|
21
17
|
{
|
|
22
18
|
sourceEventName: "toolkit-ready",
|
|
23
19
|
instrumentationEventName: "pie-toolkit-ready",
|
|
@@ -32,12 +28,10 @@ export const TOOLKIT_INSTRUMENTATION_EVENT_MAP = [
|
|
|
32
28
|
},
|
|
33
29
|
];
|
|
34
30
|
export const SECTION_INSTRUMENTATION_EVENT_MAP = [
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
// `ready`) and the `section-controller-ready` mapping were removed
|
|
40
|
-
// alongside their DOM-event surfaces.
|
|
31
|
+
// The runtime engine dispatches `pie-stage-change` and
|
|
32
|
+
// `pie-loading-complete` on the layout element; `framework-error`
|
|
33
|
+
// bubbles to it from the player's toolkit. `session-changed` stays off
|
|
34
|
+
// the bridge for the reason the item map gives below.
|
|
41
35
|
{
|
|
42
36
|
sourceEventName: "pie-stage-change",
|
|
43
37
|
instrumentationEventName: "pie-section-stage-change",
|
|
@@ -46,14 +40,6 @@ export const SECTION_INSTRUMENTATION_EVENT_MAP = [
|
|
|
46
40
|
sourceEventName: "pie-loading-complete",
|
|
47
41
|
instrumentationEventName: "pie-section-loading-complete",
|
|
48
42
|
},
|
|
49
|
-
{
|
|
50
|
-
sourceEventName: "session-changed",
|
|
51
|
-
instrumentationEventName: "pie-section-session-changed",
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
sourceEventName: "composition-changed",
|
|
55
|
-
instrumentationEventName: "pie-section-composition-changed",
|
|
56
|
-
},
|
|
57
43
|
{
|
|
58
44
|
sourceEventName: "framework-error",
|
|
59
45
|
instrumentationEventName: "pie-section-framework-error",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { InstrumentationProvider } from "../instrumentation/types.js";
|
|
1
2
|
export declare function resolveInstrumentationProvider(args: {
|
|
2
3
|
runtimePlayer?: unknown;
|
|
3
4
|
player?: unknown;
|
|
4
5
|
debug?: boolean;
|
|
5
6
|
component?: string;
|
|
6
|
-
}):
|
|
7
|
+
}): InstrumentationProvider | undefined;
|
|
@@ -37,7 +37,6 @@ const logger = createPieLogger("instrumentation-provider-resolution", () => isGl
|
|
|
37
37
|
function getDefaultInstrumentationProvider() {
|
|
38
38
|
if (!defaultProvider) {
|
|
39
39
|
defaultProvider = new NewRelicInstrumentationProvider();
|
|
40
|
-
// New Relic provider sets readiness based on window.newrelic.
|
|
41
40
|
void defaultProvider.initialize();
|
|
42
41
|
}
|
|
43
42
|
return defaultProvider;
|
|
@@ -45,7 +45,11 @@ export class ItemController {
|
|
|
45
45
|
setSession(input, options = {}) {
|
|
46
46
|
const next = normalizeSessionContainer(input, this.sessionId);
|
|
47
47
|
const allowMetadataOverwrite = options.allowMetadataOverwrite ?? false;
|
|
48
|
+
// The guard keeps a response from metadata churn on the same session. A
|
|
49
|
+
// session with another id is a new attempt or item, and replaces it.
|
|
50
|
+
const sameSession = next.id === "" || this.session.id === "" || next.id === this.session.id;
|
|
48
51
|
if (!allowMetadataOverwrite &&
|
|
52
|
+
sameSession &&
|
|
49
53
|
hasResponseValue(this.session) &&
|
|
50
54
|
!hasResponseValue(next) &&
|
|
51
55
|
!hasResponseField(next)) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SessionCommitReason } from "./session-commit.js";
|
|
1
2
|
export type ItemSessionContainer = {
|
|
2
3
|
id: string;
|
|
3
4
|
data: unknown[];
|
|
@@ -8,7 +9,14 @@ export type NormalizedItemSessionChange = {
|
|
|
8
9
|
session: ItemSessionContainer | null;
|
|
9
10
|
intent: ItemSessionUpdateIntent;
|
|
10
11
|
component?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The id of the element whose change this is: the detail's `elementId`, or
|
|
14
|
+
* the `id` of an element session record carried as `session`.
|
|
15
|
+
*/
|
|
16
|
+
elementId?: string;
|
|
11
17
|
complete?: boolean;
|
|
18
|
+
/** The detail's `sessionCommitReason`, when the change is a commit. */
|
|
19
|
+
sessionCommitReason?: SessionCommitReason;
|
|
12
20
|
};
|
|
13
21
|
export declare function normalizeItemSessionContainer(input: unknown, fallbackSessionId?: string): ItemSessionContainer;
|
|
14
22
|
export declare function hasResponseValue(value: unknown): boolean;
|
|
@@ -2,6 +2,7 @@ const DEFAULT_SESSION_ID = "";
|
|
|
2
2
|
const METADATA_ONLY_SESSION_KEYS = new Set([
|
|
3
3
|
"complete",
|
|
4
4
|
"component",
|
|
5
|
+
"elementId",
|
|
5
6
|
"timestamp",
|
|
6
7
|
"sourceRuntimeId",
|
|
7
8
|
]);
|
|
@@ -162,6 +163,18 @@ function getMetadataComplete(sessionDetail, candidate) {
|
|
|
162
163
|
return candidate.complete;
|
|
163
164
|
return undefined;
|
|
164
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* `elementRecord` is an element's own session record, whose `id` is the
|
|
168
|
+
* element's; an item session container's `id` is the item session's.
|
|
169
|
+
*/
|
|
170
|
+
function withElementId(sessionDetail, elementRecord) {
|
|
171
|
+
const elementId = typeof sessionDetail.elementId === "string" && sessionDetail.elementId
|
|
172
|
+
? sessionDetail.elementId
|
|
173
|
+
: typeof elementRecord?.id === "string" && elementRecord.id
|
|
174
|
+
? elementRecord.id
|
|
175
|
+
: undefined;
|
|
176
|
+
return elementId ? { elementId } : {};
|
|
177
|
+
}
|
|
165
178
|
function isElementIdentityOnlyPayload(candidate) {
|
|
166
179
|
const candidateKeys = Object.keys(candidate);
|
|
167
180
|
const hasIdentityKey = "id" in candidate || "element" in candidate;
|
|
@@ -211,6 +224,14 @@ export function mergeElementIntoSession(itemId, previousItemSession, entryId, en
|
|
|
211
224
|
};
|
|
212
225
|
}
|
|
213
226
|
export function normalizeItemSessionChange(args) {
|
|
227
|
+
const change = classifyItemSessionChange(args);
|
|
228
|
+
const reason = args.sessionDetail
|
|
229
|
+
?.sessionCommitReason;
|
|
230
|
+
return typeof reason === "string" && reason
|
|
231
|
+
? { ...change, sessionCommitReason: reason }
|
|
232
|
+
: change;
|
|
233
|
+
}
|
|
234
|
+
function classifyItemSessionChange(args) {
|
|
214
235
|
const sessionDetail = (args.sessionDetail || {});
|
|
215
236
|
const actualSession = sessionDetail &&
|
|
216
237
|
typeof sessionDetail === "object" &&
|
|
@@ -229,6 +250,7 @@ export function normalizeItemSessionChange(args) {
|
|
|
229
250
|
session: null,
|
|
230
251
|
intent: "metadata-only",
|
|
231
252
|
component: getMetadataComponent(sessionDetail),
|
|
253
|
+
...withElementId(sessionDetail),
|
|
232
254
|
complete: getMetadataComplete(sessionDetail),
|
|
233
255
|
};
|
|
234
256
|
}
|
|
@@ -241,11 +263,7 @@ export function normalizeItemSessionChange(args) {
|
|
|
241
263
|
const previousForMetadataComparison = alignPreviousSessionIdForRendererSnapshot(normalizedCandidate, previousNormalized);
|
|
242
264
|
const sessionDetailKeys = Object.keys(sessionDetail);
|
|
243
265
|
const metadataWithSessionOnly = sessionDetailKeys.length > 0 &&
|
|
244
|
-
sessionDetailKeys.every((key) => key === "session" ||
|
|
245
|
-
key === "complete" ||
|
|
246
|
-
key === "component" ||
|
|
247
|
-
key === "timestamp" ||
|
|
248
|
-
key === "sourceRuntimeId");
|
|
266
|
+
sessionDetailKeys.every((key) => key === "session" || METADATA_ONLY_SESSION_KEYS.has(key));
|
|
249
267
|
const hasMetadataBeyondSession = sessionDetailKeys.some((key) => key !== "session");
|
|
250
268
|
if (metadataWithSessionOnly &&
|
|
251
269
|
hasMetadataBeyondSession &&
|
|
@@ -256,6 +274,7 @@ export function normalizeItemSessionChange(args) {
|
|
|
256
274
|
session: null,
|
|
257
275
|
intent: "metadata-only",
|
|
258
276
|
component: getMetadataComponent(sessionDetail),
|
|
277
|
+
...withElementId(sessionDetail),
|
|
259
278
|
complete: getMetadataComplete(sessionDetail),
|
|
260
279
|
};
|
|
261
280
|
}
|
|
@@ -266,6 +285,7 @@ export function normalizeItemSessionChange(args) {
|
|
|
266
285
|
session: null,
|
|
267
286
|
intent: "metadata-only",
|
|
268
287
|
component: getMetadataComponent(sessionDetail),
|
|
288
|
+
...withElementId(sessionDetail),
|
|
269
289
|
complete: getMetadataComplete(sessionDetail),
|
|
270
290
|
};
|
|
271
291
|
}
|
|
@@ -274,21 +294,20 @@ export function normalizeItemSessionChange(args) {
|
|
|
274
294
|
session: normalizedCandidate,
|
|
275
295
|
intent: "replace-item-session",
|
|
276
296
|
component: getMetadataComponent(sessionDetail),
|
|
297
|
+
...withElementId(sessionDetail),
|
|
277
298
|
complete: getMetadataComplete(sessionDetail),
|
|
278
299
|
};
|
|
279
300
|
}
|
|
280
301
|
const candidateKeys = Object.keys(candidate);
|
|
281
302
|
const isMetadataOnlyPayload = candidateKeys.length > 0 &&
|
|
282
|
-
candidateKeys.every((key) => key
|
|
283
|
-
key === "component" ||
|
|
284
|
-
key === "timestamp" ||
|
|
285
|
-
key === "sourceRuntimeId");
|
|
303
|
+
candidateKeys.every((key) => METADATA_ONLY_SESSION_KEYS.has(key));
|
|
286
304
|
if (isMetadataOnlyPayload) {
|
|
287
305
|
return {
|
|
288
306
|
itemId: safeItemId,
|
|
289
307
|
session: null,
|
|
290
308
|
intent: "metadata-only",
|
|
291
309
|
component: getMetadataComponent(sessionDetail, candidate),
|
|
310
|
+
...withElementId(sessionDetail),
|
|
292
311
|
complete: getMetadataComplete(sessionDetail, candidate),
|
|
293
312
|
};
|
|
294
313
|
}
|
|
@@ -298,6 +317,7 @@ export function normalizeItemSessionChange(args) {
|
|
|
298
317
|
session: null,
|
|
299
318
|
intent: "metadata-only",
|
|
300
319
|
component: getMetadataComponent(sessionDetail, candidate),
|
|
320
|
+
...withElementId(sessionDetail, candidate),
|
|
301
321
|
complete: getMetadataComplete(sessionDetail, candidate),
|
|
302
322
|
};
|
|
303
323
|
}
|
|
@@ -309,6 +329,7 @@ export function normalizeItemSessionChange(args) {
|
|
|
309
329
|
session: merged,
|
|
310
330
|
intent: "merge-element-session",
|
|
311
331
|
component,
|
|
332
|
+
...withElementId(sessionDetail, candidate),
|
|
312
333
|
complete: getMetadataComplete(sessionDetail, candidate),
|
|
313
334
|
};
|
|
314
335
|
}
|
package/dist/pie/logger.d.ts
CHANGED
|
@@ -61,3 +61,9 @@ declare global {
|
|
|
61
61
|
* Check if debug mode is enabled globally
|
|
62
62
|
*/
|
|
63
63
|
export declare function isGlobalDebugEnabled(): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Read-aloud's debug flag: `PIE_TTS_DEBUG=1` in the environment, or
|
|
66
|
+
* `globalThis.__PIE_TTS_DEBUG__ = true` in a page. Every read-aloud logger is
|
|
67
|
+
* created with it, and reads it on each debug line.
|
|
68
|
+
*/
|
|
69
|
+
export declare function isTtsDebugEnabled(): boolean;
|
package/dist/pie/logger.js
CHANGED
|
@@ -42,3 +42,13 @@ export function createPieLogger(namespace, debugEnabledFn) {
|
|
|
42
42
|
export function isGlobalDebugEnabled() {
|
|
43
43
|
return typeof window !== "undefined" && window.PIE_DEBUG === true;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Read-aloud's debug flag: `PIE_TTS_DEBUG=1` in the environment, or
|
|
47
|
+
* `globalThis.__PIE_TTS_DEBUG__ = true` in a page. Every read-aloud logger is
|
|
48
|
+
* created with it, and reads it on each debug line.
|
|
49
|
+
*/
|
|
50
|
+
export function isTtsDebugEnabled() {
|
|
51
|
+
const scope = globalThis;
|
|
52
|
+
return (scope.process?.env?.PIE_TTS_DEBUG === "1" ||
|
|
53
|
+
scope.__PIE_TTS_DEBUG__ === true);
|
|
54
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Math in an item's own markup: what the item and passage markup carry outside
|
|
3
|
+
* every PIE element.
|
|
4
|
+
*
|
|
5
|
+
* Each element typesets its own subtree, so the player hands its renderer only
|
|
6
|
+
* the markup around them. A renderer finds math below the element it is given,
|
|
7
|
+
* so no root holds a PIE element: the markup splits into the elements that hold
|
|
8
|
+
* none, and math sharing a parent with one is wrapped in a span of its own.
|
|
9
|
+
*/
|
|
10
|
+
import type { MathRenderer } from "./math-rendering.js";
|
|
11
|
+
/**
|
|
12
|
+
* The parts of `container` holding math and no PIE element, each one a root to
|
|
13
|
+
* typeset. `pieTags` are the tag names of the PIE elements its markup places.
|
|
14
|
+
*/
|
|
15
|
+
export declare function collectMarkupMathRoots(container: HTMLElement, pieTags: ReadonlySet<string>): HTMLElement[];
|
|
16
|
+
/**
|
|
17
|
+
* Typesets the math in `containers` outside every PIE element with `render`,
|
|
18
|
+
* one root at a time.
|
|
19
|
+
*/
|
|
20
|
+
export declare function typesetMarkupMath(containers: Iterable<HTMLElement>, pieTags: ReadonlySet<string>, render: MathRenderer): Promise<void>;
|