@pie-players/pie-players-shared 0.3.74 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +88 -75
- package/dist/components/vendor/nds/README.md +4 -5
- package/dist/config/profile.d.ts +1 -1
- package/dist/config/profile.js +1 -1
- package/dist/i18n/index.d.ts +1 -1
- package/dist/i18n/index.js +1 -1
- package/dist/i18n/language-tags.d.ts +9 -1
- package/dist/i18n/language-tags.js +11 -1
- package/dist/i18n/messages/en-US.d.ts +33 -28
- package/dist/i18n/messages/en-US.js +34 -29
- package/dist/i18n/messages/nl-NL.d.ts +33 -28
- package/dist/i18n/messages/nl-NL.js +34 -29
- package/dist/i18n/provider.d.ts +14 -7
- package/dist/i18n/provider.js +26 -29
- package/dist/i18n/types.d.ts +4 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.js +0 -1
- package/dist/instrumentation/providers/BaseInstrumentationProvider.js +0 -14
- package/dist/loader-config.d.ts +0 -2
- package/dist/loader-config.js +0 -2
- package/dist/loaders/element-loader.js +13 -6
- package/dist/loaders/esm-adapter.d.ts +11 -0
- package/dist/loaders/esm-adapter.js +74 -45
- package/dist/loaders/iife-adapter.d.ts +3 -3
- package/dist/loaders/iife-adapter.js +36 -35
- package/dist/loaders/index.d.ts +4 -2
- package/dist/loaders/index.js +2 -1
- package/dist/loaders/mathjax-events.d.ts +22 -0
- package/dist/loaders/mathjax-events.js +65 -0
- package/dist/loaders/preloaded-registration.d.ts +18 -2
- package/dist/loaders/preloaded-registration.js +14 -2
- package/dist/media/index.d.ts +4 -6
- package/dist/media/index.js +6 -8
- package/dist/object/index.d.ts +8 -3
- package/dist/object/index.js +8 -6
- package/dist/pie/asset-handler.d.ts +0 -17
- package/dist/pie/asset-handler.js +1 -1
- package/dist/pie/authoring.js +5 -7
- package/dist/pie/elements-rendered.d.ts +23 -0
- package/dist/pie/elements-rendered.js +96 -0
- package/dist/pie/index.d.ts +4 -6
- package/dist/pie/index.js +4 -7
- package/dist/pie/initialization.d.ts +13 -14
- package/dist/pie/initialization.js +61 -75
- package/dist/pie/instrumentation-event-bridge.js +23 -1
- package/dist/pie/instrumentation-event-map.js +11 -18
- package/dist/pie/item-controller.js +4 -0
- package/dist/pie/item-session-contract.js +7 -9
- package/dist/pie/logger.d.ts +6 -0
- package/dist/pie/logger.js +10 -0
- package/dist/pie/math-assets.d.ts +48 -0
- package/dist/pie/math-assets.js +81 -0
- package/dist/pie/resource-monitor.d.ts +34 -32
- package/dist/pie/resource-monitor.js +152 -231
- package/dist/pie/scoring.d.ts +1 -6
- package/dist/pie/scoring.js +3 -8
- package/dist/pie/session-commit.js +1 -1
- package/dist/pie/stages.d.ts +11 -47
- package/dist/pie/stages.js +11 -47
- package/dist/pie/updates.js +5 -4
- package/dist/pie/utils.d.ts +0 -2
- package/dist/pie/utils.js +0 -2
- package/dist/security/sanitize-item-markup.d.ts +8 -4
- package/dist/security/sanitize-item-markup.js +52 -6
- package/dist/security/sanitize-svg-icon.js +2 -2
- package/dist/security/wrap-overwide-images.d.ts +1 -1
- package/dist/security/wrap-overwide-images.js +20 -1
- package/dist/security/wrap-overwide.d.ts +8 -0
- package/dist/security/wrap-overwide.js +47 -3
- package/dist/timed-media/media-element-source.js +1 -2
- package/dist/types/index.d.ts +88 -153
- package/dist/types/index.js +2 -2
- package/dist/ui/attribute-coercion.d.ts +5 -0
- package/dist/ui/attribute-coercion.js +6 -4
- package/dist/ui/content-styles.d.ts +6 -10
- package/dist/ui/content-styles.js +8 -13
- package/dist/ui/first-focusable.d.ts +0 -1
- package/dist/ui/first-focusable.js +0 -1
- package/dist/ui/focus-trap.js +2 -3
- package/dist/ui/scope-css.d.ts +23 -26
- package/dist/ui/scope-css.js +23 -26
- package/package.json +6 -4
- package/dist/loaders/mathjax-version-conflict.d.ts +0 -17
- package/dist/loaders/mathjax-version-conflict.js +0 -51
- package/dist/pie/stage-tracker.d.ts +0 -50
- package/dist/pie/stage-tracker.js +0 -105
package/dist/object/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Merges objects left to right, skipping properties whose value is `null` or
|
|
3
|
+
* `undefined`, so a later object's unset field never overrides an earlier
|
|
4
|
+
* default the way object spread does.
|
|
5
5
|
* @param objects The objects to merge.
|
|
6
6
|
*/
|
|
7
7
|
export declare const mergeObjectsIgnoringNullUndefined: <T extends object>(...objects: T[]) => T;
|
|
@@ -9,3 +9,8 @@ export declare const mergeObjectsIgnoringNullUndefined: <T extends object>(...ob
|
|
|
9
9
|
* Deep clone an object.
|
|
10
10
|
*/
|
|
11
11
|
export declare const cloneDeep: <T>(value: T) => T;
|
|
12
|
+
/**
|
|
13
|
+
* A non-null object that is not an array. The prototype is not checked, so
|
|
14
|
+
* class instances pass.
|
|
15
|
+
*/
|
|
16
|
+
export declare const isPlainRecord: (value: unknown) => value is Record<string, unknown>;
|
package/dist/object/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Merges objects left to right, skipping properties whose value is `null` or
|
|
3
|
+
* `undefined`, so a later object's unset field never overrides an earlier
|
|
4
|
+
* default the way object spread does.
|
|
5
5
|
* @param objects The objects to merge.
|
|
6
6
|
*/
|
|
7
7
|
export const mergeObjectsIgnoringNullUndefined = (...objects) => {
|
|
@@ -21,14 +21,11 @@ export const mergeObjectsIgnoringNullUndefined = (...objects) => {
|
|
|
21
21
|
*/
|
|
22
22
|
export const cloneDeep = (value) => {
|
|
23
23
|
if (value === null || typeof value !== "object") {
|
|
24
|
-
// Primitive value (or null), just return it
|
|
25
24
|
return value;
|
|
26
25
|
}
|
|
27
26
|
if (Array.isArray(value)) {
|
|
28
|
-
// Array: clone each element recursively
|
|
29
27
|
return value.map(cloneDeep);
|
|
30
28
|
}
|
|
31
|
-
// Object: create a new object and clone its properties
|
|
32
29
|
const copy = {};
|
|
33
30
|
for (const key in value) {
|
|
34
31
|
if (Object.hasOwn(value, key)) {
|
|
@@ -37,3 +34,8 @@ export const cloneDeep = (value) => {
|
|
|
37
34
|
}
|
|
38
35
|
return copy;
|
|
39
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* A non-null object that is not an array. The prototype is not checked, so
|
|
39
|
+
* class instances pass.
|
|
40
|
+
*/
|
|
41
|
+
export const isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -5,23 +5,6 @@
|
|
|
5
5
|
* Provides default DataURL-based implementation and event management.
|
|
6
6
|
*/
|
|
7
7
|
import { type ImageHandler, type SoundHandler } from "../types/index.js";
|
|
8
|
-
import type { AssetHandler } from "./types.js";
|
|
9
|
-
/**
|
|
10
|
-
* DataURL-based asset handler (default implementation)
|
|
11
|
-
*
|
|
12
|
-
* Converts uploaded files to base64 data URLs.
|
|
13
|
-
* Suitable for demos and local development.
|
|
14
|
-
*/
|
|
15
|
-
export declare class DataURLAssetHandler implements AssetHandler {
|
|
16
|
-
private onComplete;
|
|
17
|
-
private cancelled;
|
|
18
|
-
private fileReader;
|
|
19
|
-
constructor(onComplete: (src: string) => void);
|
|
20
|
-
cancel(): void;
|
|
21
|
-
done(err?: Error, src?: string): void;
|
|
22
|
-
fileChosen(file: File): void;
|
|
23
|
-
progress(percent: number, bytes: number, total: number): void;
|
|
24
|
-
}
|
|
25
8
|
/**
|
|
26
9
|
* Asset Event Manager
|
|
27
10
|
*
|
|
@@ -13,7 +13,7 @@ const logger = createPieLogger("asset-handler", () => isGlobalDebugEnabled());
|
|
|
13
13
|
* Converts uploaded files to base64 data URLs.
|
|
14
14
|
* Suitable for demos and local development.
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
class DataURLAssetHandler {
|
|
17
17
|
onComplete;
|
|
18
18
|
cancelled = false;
|
|
19
19
|
fileReader = null;
|
package/dist/pie/authoring.js
CHANGED
|
@@ -2,10 +2,8 @@ import { AssetEventManager } from "./asset-handler.js";
|
|
|
2
2
|
import { createPieLogger, isGlobalDebugEnabled } from "./logger.js";
|
|
3
3
|
import { findPieController } from "./scoring.js";
|
|
4
4
|
import { parsePackageName } from "./utils.js";
|
|
5
|
+
import { isPlainRecord } from "../object/index.js";
|
|
5
6
|
const logger = createPieLogger("authoring", () => isGlobalDebugEnabled());
|
|
6
|
-
function isRecord(value) {
|
|
7
|
-
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
8
|
-
}
|
|
9
7
|
function packageNameWithoutVersion(packageSpec) {
|
|
10
8
|
try {
|
|
11
9
|
return parsePackageName(packageSpec).name;
|
|
@@ -19,7 +17,7 @@ function packageBaseName(packageName) {
|
|
|
19
17
|
return lastSegment || packageName;
|
|
20
18
|
}
|
|
21
19
|
function lookupOwn(source, keys) {
|
|
22
|
-
if (!
|
|
20
|
+
if (!isPlainRecord(source))
|
|
23
21
|
return undefined;
|
|
24
22
|
for (const key of keys) {
|
|
25
23
|
if (Object.hasOwn(source, key)) {
|
|
@@ -32,7 +30,7 @@ function mergeConfiguration(deliveryConfig, authoringConfig) {
|
|
|
32
30
|
if (authoringConfig === undefined) {
|
|
33
31
|
return deliveryConfig ?? {};
|
|
34
32
|
}
|
|
35
|
-
if (
|
|
33
|
+
if (isPlainRecord(deliveryConfig) && isPlainRecord(authoringConfig)) {
|
|
36
34
|
return {
|
|
37
35
|
...deliveryConfig,
|
|
38
36
|
...authoringConfig,
|
|
@@ -108,9 +106,9 @@ function resolveValidationController(configureElement) {
|
|
|
108
106
|
// A controller's `validate` returns a field → message map. ebsr returns one map
|
|
109
107
|
// per part, so an empty ebsr result is `{ partA: {}, partB: {} }`.
|
|
110
108
|
function hasValidationErrors(errors) {
|
|
111
|
-
if (!
|
|
109
|
+
if (!isPlainRecord(errors))
|
|
112
110
|
return false;
|
|
113
|
-
if (
|
|
111
|
+
if (isPlainRecord(errors.partA) && isPlainRecord(errors.partB)) {
|
|
114
112
|
return (Object.keys(errors.partA).length > 0 ||
|
|
115
113
|
Object.keys(errors.partB).length > 0);
|
|
116
114
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
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
|
+
import type { ConfigEntity } from "../types/index.js";
|
|
19
|
+
/**
|
|
20
|
+
* Resolves once every element of `configs` in `root` has rendered, or when
|
|
21
|
+
* `signal` aborts. Never rejects.
|
|
22
|
+
*/
|
|
23
|
+
export declare function elementsRendered(root: Element, configs: ConfigEntity[], signal: AbortSignal, quietMs?: number): Promise<void>;
|
|
@@ -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
|
+
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
|
@@ -18,16 +18,14 @@ 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
31
|
export { SESSION_COMMIT_METHOD, bindPageLifecycleCommit, commitPendingSessions, flushPendingSessionNotifications, noteSessionBaseline, noteSessionObserved, } from "./session-commit.js";
|
|
@@ -35,7 +33,7 @@ export type { BindPageLifecycleCommitOptions, CommitPendingSessionsOptions, Comm
|
|
|
35
33
|
export { createSessionSnapshot, isSessionSnapshotEnabled, isSnapshotIdentitySpecific, sessionSnapshotKey, sessionStorageSnapshotStore, } from "./session-snapshot.js";
|
|
36
34
|
export type { SessionSnapshot, SessionSnapshotConfig, SessionSnapshotIdentity, SessionSnapshotRecord, SessionSnapshotStore, } from "./session-snapshot.js";
|
|
37
35
|
export type { PieLogger } from "./logger.js";
|
|
38
|
-
export { createPieLogger, isGlobalDebugEnabled } from "./logger.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
|
@@ -23,20 +23,17 @@ export { createAuthoringAssetEventManager, initializeAuthoringConfigures, resolv
|
|
|
23
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
32
|
export { SESSION_COMMIT_METHOD, bindPageLifecycleCommit, commitPendingSessions, flushPendingSessionNotifications, noteSessionBaseline, noteSessionObserved, } from "./session-commit.js";
|
|
36
33
|
// Opt-in device-local snapshot of a committed session, offered back to the host
|
|
37
34
|
// after a crash rather than applied.
|
|
38
35
|
export { createSessionSnapshot, isSessionSnapshotEnabled, isSnapshotIdentitySpecific, sessionSnapshotKey, sessionStorageSnapshotStore, } from "./session-snapshot.js";
|
|
39
|
-
export { createPieLogger, isGlobalDebugEnabled } from "./logger.js";
|
|
36
|
+
export { createPieLogger, isGlobalDebugEnabled, isTtsDebugEnabled, } from "./logger.js";
|
|
40
37
|
// Registry
|
|
41
38
|
export { pieRegistry } from "./registry.js";
|
|
42
39
|
// Scoring
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PIE Initialization Module
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Loads IIFE bundles into `window.pie`, defines their elements and records
|
|
5
|
+
* them in the PIE registry, and binds config and session to the elements in
|
|
6
|
+
* the container.
|
|
6
7
|
*/
|
|
7
8
|
import type { ConfigEntity } from "../types/index.js";
|
|
8
9
|
import type { LoadPieElementsOptions } from "./types.js";
|
|
@@ -26,13 +27,11 @@ import type { LoadPieElementsOptions } from "./types.js";
|
|
|
26
27
|
* the existing `MutationObserver` / `updatePieElements` flow binds
|
|
27
28
|
* them on the next reactive pass.
|
|
28
29
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* gets its own warning, and `updatePieElements` later reports any tag
|
|
35
|
-
* that never resolves.
|
|
30
|
+
* A missing `window.pie` raises no error: in the section-player and
|
|
31
|
+
* item-player composition it is a routine timing condition the host already
|
|
32
|
+
* handles. A bundle nobody loaded still surfaces, as a warning per
|
|
33
|
+
* unregistered tag and as an `updatePieElements` report for any tag that
|
|
34
|
+
* never resolves.
|
|
36
35
|
*
|
|
37
36
|
* Binds the elements present in `opts.container` now. Elements that arrive
|
|
38
37
|
* later are the container owner's concern: a player with a lifecycle calls
|
|
@@ -53,13 +52,13 @@ export declare const loadPieModule: (config: ConfigEntity, session: any[], opts?
|
|
|
53
52
|
session: any[];
|
|
54
53
|
}>;
|
|
55
54
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
55
|
+
* Executes an IIFE bundle from a JavaScript string, which populates
|
|
56
|
+
* `window.pie`. Defines no elements: `initializePiesFromLoadedBundle`
|
|
57
|
+
* registers and binds them afterwards.
|
|
59
58
|
*/
|
|
60
59
|
export declare const loadBundleFromString: (bundleJs: string) => Promise<void>;
|
|
61
60
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
61
|
+
* Loads a PIE bundle from a JavaScript string and initializes its elements:
|
|
62
|
+
* `loadPieModule` over a blob URL of the source.
|
|
64
63
|
*/
|
|
65
64
|
export declare const loadPieModuleFromString: (bundleJs: string, config: ConfigEntity, session: any[], opts?: LoadPieElementsOptions) => Promise<void>;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* PIE Initialization Module
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Loads IIFE bundles into `window.pie`, defines their elements and records
|
|
5
|
+
* them in the PIE registry, and binds config and session to the elements in
|
|
6
|
+
* the container.
|
|
6
7
|
*/
|
|
7
8
|
import { BUILDER_BUNDLE_URL } from "../config/profile.js";
|
|
8
9
|
import { DEFAULT_IIFE_BUNDLE_RETRY_CONFIG } from "../loader-config.js";
|
|
@@ -17,7 +18,6 @@ import { validateCustomElementTag } from "./tag-names.js";
|
|
|
17
18
|
import { BundleType, isCustomElementConstructor, isPieAvailable, Status, } from "./types.js";
|
|
18
19
|
import { updatePieElement } from "./updates.js";
|
|
19
20
|
import { getPackageWithoutVersion, getPieElementBundlesUrl, } from "./utils.js";
|
|
20
|
-
// Create module-level logger (respects global debug flag - pass function for dynamic checking)
|
|
21
21
|
const logger = createPieLogger("pie-initialization", () => isGlobalDebugEnabled());
|
|
22
22
|
/**
|
|
23
23
|
* Deadline for `loadPieModule`'s bundle `<script>` load when the caller
|
|
@@ -26,7 +26,6 @@ const logger = createPieLogger("pie-initialization", () => isGlobalDebugEnabled(
|
|
|
26
26
|
* path a host loads it through.
|
|
27
27
|
*/
|
|
28
28
|
const DEFAULT_LOAD_TIMEOUT_MS = DEFAULT_IIFE_BUNDLE_RETRY_CONFIG.timeoutMs;
|
|
29
|
-
// Default options for loading PIE elements
|
|
30
29
|
const defaultOptions = {
|
|
31
30
|
buildServiceBase: BUILDER_BUNDLE_URL,
|
|
32
31
|
bundleType: BundleType.player, // Default to player.js (no controllers, server-processed models)
|
|
@@ -46,12 +45,10 @@ const updateRegisteredElement = (elementTagName, config, session, options, omitE
|
|
|
46
45
|
});
|
|
47
46
|
};
|
|
48
47
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* arrive later are bound by the container owner's observer — see
|
|
54
|
-
* `element-observer.ts`.
|
|
48
|
+
* Defines the bundle's elements for `config.elements`, records them in the
|
|
49
|
+
* registry, and binds the elements already present in `options.container`.
|
|
50
|
+
* Elements that arrive later are bound by the container owner's observer —
|
|
51
|
+
* see `element-observer.ts`. Returns one promise per defined tag.
|
|
55
52
|
*
|
|
56
53
|
* `elementModule` may be `null`. In that case we cannot register *new*
|
|
57
54
|
* tags (no element constructor source), but we can still update tags
|
|
@@ -120,59 +117,55 @@ const registerPieElementsFromBundle = (elementModule, config, session, registry,
|
|
|
120
117
|
: [],
|
|
121
118
|
bundleType: options.bundleType,
|
|
122
119
|
});
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
120
|
+
// A package without a controller, such as a legacy
|
|
121
|
+
// `@pie-element/protractor`, renders the model it is given under
|
|
122
|
+
// client-player.js too.
|
|
123
|
+
logger.debug(`[registerPieElementsFromBundle] Registering ${elName} in registry${elementData.controller
|
|
124
|
+
? " with controller"
|
|
125
|
+
: " (no controller; its model is used as given)"}`);
|
|
126
|
+
writeRegistryEntry({
|
|
127
|
+
package: pkg,
|
|
128
|
+
status: Status.loading,
|
|
129
|
+
tagName: elementTagName,
|
|
130
|
+
controller: elementData.controller || null,
|
|
131
|
+
config: elementData.config,
|
|
132
|
+
bundleType: options.bundleType,
|
|
133
|
+
});
|
|
134
|
+
if (!isCustomElementConstructor(elementData.Element)) {
|
|
135
|
+
logger.error(`[registerPieElementsFromBundle] pie.Element for ${pkgStripped} is not a valid custom element constructor.`);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
defineCustomElementSafely(elementTagName, elementData.Element, `element tag in config.elements for ${String(pkg)}`);
|
|
139
|
+
// Initialize existing elements
|
|
140
|
+
const searchRoot = options.container || document;
|
|
141
|
+
const elements = searchRoot.querySelectorAll(elementTagName);
|
|
142
|
+
logger.debug(`[registerPieElementsFromBundle] Found ${elements.length} elements for tag '${elementTagName}'`);
|
|
143
|
+
elements.forEach((el) => {
|
|
144
|
+
initializePieElement(el, {
|
|
145
|
+
config,
|
|
146
|
+
session,
|
|
147
|
+
env: options.env,
|
|
148
|
+
eventListeners: options.eventListeners?.[elementTagName],
|
|
136
149
|
bundleType: options.bundleType,
|
|
137
150
|
});
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
writeRegistryEntry({
|
|
154
|
-
...registry[elementTagName],
|
|
155
|
-
status: Status.loaded,
|
|
156
|
-
});
|
|
157
|
-
promises.push(customElements.whenDefined(elementTagName).then(() => {
|
|
158
|
-
logger.debug("[registerPieElementsFromBundle] defined custom PIE element: %s", elementTagName);
|
|
151
|
+
});
|
|
152
|
+
writeRegistryEntry({
|
|
153
|
+
...registry[elementTagName],
|
|
154
|
+
status: Status.loaded,
|
|
155
|
+
});
|
|
156
|
+
promises.push(customElements.whenDefined(elementTagName).then(() => {
|
|
157
|
+
logger.debug("[registerPieElementsFromBundle] defined custom PIE element: %s", elementTagName);
|
|
158
|
+
}));
|
|
159
|
+
if (options.bundleType === BundleType.editor) {
|
|
160
|
+
if (isCustomElementConstructor(elementData.Configure)) {
|
|
161
|
+
const editorElName = getEditorElementTagName(elementTagName, String(pkg));
|
|
162
|
+
defineCustomElementSafely(editorElName, elementData.Configure, `editor element tag for ${String(pkg)}`);
|
|
163
|
+
promises.push(customElements.whenDefined(editorElName).then(() => {
|
|
164
|
+
logger.debug(`[registerPieElementsFromBundle] defined custom PIE editor element: ${editorElName}`);
|
|
159
165
|
}));
|
|
160
|
-
// Handle editor elements if needed
|
|
161
|
-
if (options.bundleType === BundleType.editor) {
|
|
162
|
-
if (isCustomElementConstructor(elementData.Configure)) {
|
|
163
|
-
const editorElName = getEditorElementTagName(elementTagName, String(pkg));
|
|
164
|
-
defineCustomElementSafely(editorElName, elementData.Configure, `editor element tag for ${String(pkg)}`);
|
|
165
|
-
promises.push(customElements.whenDefined(editorElName).then(() => {
|
|
166
|
-
logger.debug(`[registerPieElementsFromBundle] defined custom PIE editor element: ${editorElName}`);
|
|
167
|
-
}));
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
logger.error(`[registerPieElementsFromBundle] pie.Configure for ${pkgStripped} is not a valid custom element constructor.`, elementData.configure);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
166
|
}
|
|
174
167
|
else {
|
|
175
|
-
logger.error(`[registerPieElementsFromBundle] pie.
|
|
168
|
+
logger.error(`[registerPieElementsFromBundle] pie.Configure for ${pkgStripped} is not a valid custom element constructor.`, elementData.configure);
|
|
176
169
|
}
|
|
177
170
|
}
|
|
178
171
|
});
|
|
@@ -198,13 +191,11 @@ const registerPieElementsFromBundle = (elementModule, config, session, registry,
|
|
|
198
191
|
* the existing `MutationObserver` / `updatePieElements` flow binds
|
|
199
192
|
* them on the next reactive pass.
|
|
200
193
|
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
* gets its own warning, and `updatePieElements` later reports any tag
|
|
207
|
-
* that never resolves.
|
|
194
|
+
* A missing `window.pie` raises no error: in the section-player and
|
|
195
|
+
* item-player composition it is a routine timing condition the host already
|
|
196
|
+
* handles. A bundle nobody loaded still surfaces, as a warning per
|
|
197
|
+
* unregistered tag and as an `updatePieElements` report for any tag that
|
|
198
|
+
* never resolves.
|
|
208
199
|
*
|
|
209
200
|
* Binds the elements present in `opts.container` now. Elements that arrive
|
|
210
201
|
* later are the container owner's concern: a player with a lifecycle calls
|
|
@@ -282,9 +273,7 @@ export const loadPieModule = async (config, session, opts = {}) => {
|
|
|
282
273
|
logger.debug("[loadPieModule] window.pie available");
|
|
283
274
|
const elementModule = window.pie.default;
|
|
284
275
|
try {
|
|
285
|
-
// Use shared registration logic (returns array of promises)
|
|
286
276
|
const registrationPromises = registerPieElementsFromBundle(elementModule, config, session, registry, options);
|
|
287
|
-
// Wait for all element definitions to complete
|
|
288
277
|
Promise.all(registrationPromises).then(succeed, fail);
|
|
289
278
|
}
|
|
290
279
|
catch (error) {
|
|
@@ -321,18 +310,16 @@ export const loadPieModule = async (config, session, opts = {}) => {
|
|
|
321
310
|
return { session };
|
|
322
311
|
};
|
|
323
312
|
/**
|
|
324
|
-
*
|
|
325
|
-
*
|
|
326
|
-
*
|
|
313
|
+
* Executes an IIFE bundle from a JavaScript string, which populates
|
|
314
|
+
* `window.pie`. Defines no elements: `initializePiesFromLoadedBundle`
|
|
315
|
+
* registers and binds them afterwards.
|
|
327
316
|
*/
|
|
328
317
|
export const loadBundleFromString = async (bundleJs) => {
|
|
329
318
|
await initializeMathRendering();
|
|
330
319
|
await withBlobBundleUrl(bundleJs, { stripSourceMapComment: true }, async (bundleUrl) => {
|
|
331
|
-
// Create a script tag to execute the bundle
|
|
332
320
|
const script = document.createElement("script");
|
|
333
321
|
script.src = bundleUrl;
|
|
334
322
|
script.type = "text/javascript"; // IIFE bundles are standard JS
|
|
335
|
-
// Wait for script to load
|
|
336
323
|
await new Promise((resolve, reject) => {
|
|
337
324
|
script.onload = () => resolve();
|
|
338
325
|
script.onerror = () => reject(new Error("Failed to load bundle"));
|
|
@@ -342,12 +329,11 @@ export const loadBundleFromString = async (bundleJs) => {
|
|
|
342
329
|
});
|
|
343
330
|
};
|
|
344
331
|
/**
|
|
345
|
-
*
|
|
346
|
-
*
|
|
332
|
+
* Loads a PIE bundle from a JavaScript string and initializes its elements:
|
|
333
|
+
* `loadPieModule` over a blob URL of the source.
|
|
347
334
|
*/
|
|
348
335
|
export const loadPieModuleFromString = async (bundleJs, config, session, opts = {}) => {
|
|
349
336
|
await withBlobBundleUrl(bundleJs, {}, async (bundleUrl) => {
|
|
350
|
-
// Use existing loadPieModule with the blob URL
|
|
351
337
|
await loadPieModule(config, session, { ...opts, bundleUrl });
|
|
352
338
|
});
|
|
353
339
|
};
|
|
@@ -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 {};
|