@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.
Files changed (86) hide show
  1. package/README.md +88 -75
  2. package/dist/components/vendor/nds/README.md +4 -5
  3. package/dist/config/profile.d.ts +1 -1
  4. package/dist/config/profile.js +1 -1
  5. package/dist/i18n/index.d.ts +1 -1
  6. package/dist/i18n/index.js +1 -1
  7. package/dist/i18n/language-tags.d.ts +9 -1
  8. package/dist/i18n/language-tags.js +11 -1
  9. package/dist/i18n/messages/en-US.d.ts +33 -28
  10. package/dist/i18n/messages/en-US.js +34 -29
  11. package/dist/i18n/messages/nl-NL.d.ts +33 -28
  12. package/dist/i18n/messages/nl-NL.js +34 -29
  13. package/dist/i18n/provider.d.ts +14 -7
  14. package/dist/i18n/provider.js +26 -29
  15. package/dist/i18n/types.d.ts +4 -5
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.js +0 -1
  18. package/dist/instrumentation/providers/BaseInstrumentationProvider.js +0 -14
  19. package/dist/loader-config.d.ts +0 -2
  20. package/dist/loader-config.js +0 -2
  21. package/dist/loaders/element-loader.js +13 -6
  22. package/dist/loaders/esm-adapter.d.ts +11 -0
  23. package/dist/loaders/esm-adapter.js +74 -45
  24. package/dist/loaders/iife-adapter.d.ts +3 -3
  25. package/dist/loaders/iife-adapter.js +36 -35
  26. package/dist/loaders/index.d.ts +4 -2
  27. package/dist/loaders/index.js +2 -1
  28. package/dist/loaders/mathjax-events.d.ts +22 -0
  29. package/dist/loaders/mathjax-events.js +65 -0
  30. package/dist/loaders/preloaded-registration.d.ts +18 -2
  31. package/dist/loaders/preloaded-registration.js +14 -2
  32. package/dist/media/index.d.ts +4 -6
  33. package/dist/media/index.js +6 -8
  34. package/dist/object/index.d.ts +8 -3
  35. package/dist/object/index.js +8 -6
  36. package/dist/pie/asset-handler.d.ts +0 -17
  37. package/dist/pie/asset-handler.js +1 -1
  38. package/dist/pie/authoring.js +5 -7
  39. package/dist/pie/elements-rendered.d.ts +23 -0
  40. package/dist/pie/elements-rendered.js +96 -0
  41. package/dist/pie/index.d.ts +4 -6
  42. package/dist/pie/index.js +4 -7
  43. package/dist/pie/initialization.d.ts +13 -14
  44. package/dist/pie/initialization.js +61 -75
  45. package/dist/pie/instrumentation-event-bridge.js +23 -1
  46. package/dist/pie/instrumentation-event-map.js +11 -18
  47. package/dist/pie/item-controller.js +4 -0
  48. package/dist/pie/item-session-contract.js +7 -9
  49. package/dist/pie/logger.d.ts +6 -0
  50. package/dist/pie/logger.js +10 -0
  51. package/dist/pie/math-assets.d.ts +48 -0
  52. package/dist/pie/math-assets.js +81 -0
  53. package/dist/pie/resource-monitor.d.ts +34 -32
  54. package/dist/pie/resource-monitor.js +152 -231
  55. package/dist/pie/scoring.d.ts +1 -6
  56. package/dist/pie/scoring.js +3 -8
  57. package/dist/pie/session-commit.js +1 -1
  58. package/dist/pie/stages.d.ts +11 -47
  59. package/dist/pie/stages.js +11 -47
  60. package/dist/pie/updates.js +5 -4
  61. package/dist/pie/utils.d.ts +0 -2
  62. package/dist/pie/utils.js +0 -2
  63. package/dist/security/sanitize-item-markup.d.ts +8 -4
  64. package/dist/security/sanitize-item-markup.js +52 -6
  65. package/dist/security/sanitize-svg-icon.js +2 -2
  66. package/dist/security/wrap-overwide-images.d.ts +1 -1
  67. package/dist/security/wrap-overwide-images.js +20 -1
  68. package/dist/security/wrap-overwide.d.ts +8 -0
  69. package/dist/security/wrap-overwide.js +47 -3
  70. package/dist/timed-media/media-element-source.js +1 -2
  71. package/dist/types/index.d.ts +88 -153
  72. package/dist/types/index.js +2 -2
  73. package/dist/ui/attribute-coercion.d.ts +5 -0
  74. package/dist/ui/attribute-coercion.js +6 -4
  75. package/dist/ui/content-styles.d.ts +6 -10
  76. package/dist/ui/content-styles.js +8 -13
  77. package/dist/ui/first-focusable.d.ts +0 -1
  78. package/dist/ui/first-focusable.js +0 -1
  79. package/dist/ui/focus-trap.js +2 -3
  80. package/dist/ui/scope-css.d.ts +23 -26
  81. package/dist/ui/scope-css.js +23 -26
  82. package/package.json +6 -4
  83. package/dist/loaders/mathjax-version-conflict.d.ts +0 -17
  84. package/dist/loaders/mathjax-version-conflict.js +0 -51
  85. package/dist/pie/stage-tracker.d.ts +0 -50
  86. package/dist/pie/stage-tracker.js +0 -105
@@ -4,37 +4,34 @@
4
4
  * external-style-urls="...">` or `itemConfig.resources.stylesheets[*].url`
5
5
  * styles that player's subtree instead of the whole host document.
6
6
  *
7
- * This replaces a single-regex implementation that prefixed every
8
- * selector-like fragment. That is correct for flat selector rules and wrong for
9
- * everything else: `@media screen { ... }` became
10
- * `.scope @media screen { ... }`, an invalid selector, so the browser dropped
11
- * the whole block and every rule inside it; `@font-face` and `@keyframes` were
12
- * corrupted the same way; and `:root` became `.scope :root`, which can never
13
- * match, because `:root` is `<html>` and is not a descendant of the player.
14
- * At-rules and `:root` custom properties therefore never applied at all.
7
+ * Walks the stylesheet brace-by-brace. Block at-rules that hold rules
8
+ * (`@media`, `@supports`, `@container`, `@layer`, `@scope`) keep their prelude
9
+ * and have their inner rules scoped, every other at-rule passes through, and a
10
+ * leading `:root`, `html` or `body` is replaced by the scope, since
11
+ * `.scope :root` can never match: `:root` is `<html>`, which is no descendant
12
+ * of the player. A regex prefixer fails on exactly these cases: it turned
13
+ * `@media screen { … }` into the invalid `.scope @media screen { … }`, which the
14
+ * browser drops with every rule inside, and corrupted `@font-face`,
15
+ * `@keyframes` and `:root` the same way.
15
16
  *
16
- * The QTI player's `scopeCssRules` (`pie-qti` -
17
- * `packages/item-player/src/components/utils/stylesheetRender.ts`) is not a
18
- * drop-in either. Its `:root` handling is the behaviour we want and is adopted
19
- * here, but it excludes `@` from its selector pattern rather than understanding
20
- * at-rules, so `@media` and `@supports` lose their condition and their inner
21
- * rules are hoisted and applied unconditionally — a mobile-only rule then
22
- * applies at every viewport width, which is a subtler failure than dropping it.
17
+ * The `:root` handling follows the QTI player's `scopeCssRules` (`pie-qti` -
18
+ * `packages/item-player/src/components/utils/stylesheetRender.ts`), which is no
19
+ * drop-in: it excludes `@` from its selector pattern without understanding
20
+ * at-rules, so `@media` and `@supports` lose their condition and their rules
21
+ * apply at every viewport width.
23
22
  *
24
- * So this walks the stylesheet brace-by-brace instead of pattern-matching it.
25
- * A real CSS parser would be the textbook answer and is deliberately not used:
26
- * `@pie-players/pie-item-player` ships with two workspace dependencies, this
27
- * runs in the delivery path, and scoping needs to know about rule boundaries
28
- * only — not about property grammar.
23
+ * No CSS parser: `@pie-players/pie-item-player` ships with two workspace
24
+ * dependencies, this runs in the delivery path, and scoping needs rule
25
+ * boundaries only.
29
26
  *
30
- * Known limits, all preserving existing behaviour rather than adding policy:
27
+ * Known limits:
31
28
  *
32
- * - `@import` is passed through untouched, exactly as the old regex left it. It
33
- * pulls in an unscoped stylesheet, so it defeats scoping, but blocking it is
34
- * a security policy decision and not this function's job. Callers gate
35
- * stylesheet URLs with `validateExternalStyleUrl` before fetching.
29
+ * - `@import` is passed through untouched. It pulls in an unscoped stylesheet,
30
+ * so it defeats scoping, but blocking it is a security policy decision and
31
+ * not this function's job. Callers gate stylesheet URLs with
32
+ * `validateExternalStyleUrl` before fetching.
36
33
  * - Declaration blocks are emitted verbatim, so `url(...)` references are left
37
- * as authored and resolve against the stylesheet's own URL as before.
34
+ * as authored and resolve against the stylesheet's own URL.
38
35
  * - A style rule's block is never rewritten, which is also what native CSS
39
36
  * nesting needs: nested selectors are relative to a parent that has already
40
37
  * been scoped.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pie-players/pie-players-shared",
3
- "version": "0.3.74",
3
+ "version": "1.0.0-beta.0",
4
4
  "type": "module",
5
5
  "description": "Shared runtime + UI utilities for PIE players",
6
6
  "license": "MIT",
@@ -102,6 +102,7 @@
102
102
  "dist"
103
103
  ],
104
104
  "dependencies": {
105
+ "@pie-element/shared-utils": "0.1.3",
105
106
  "@pie-lib/math-rendering-module": "6.0.0",
106
107
  "dompurify": "^3.4.16",
107
108
  "semver": "^7.8.5"
@@ -109,9 +110,9 @@
109
110
  "scripts": {
110
111
  "build": "rm -rf dist tsconfig.tsbuildinfo && tsc -p tsconfig.json && node scripts/bundle-vendored-modules.mjs && mkdir -p dist/components && cp -R src/components/vendor dist/components/vendor",
111
112
  "dev": "tsc -p tsconfig.json --watch",
112
- "typecheck": "tsc -p tsconfig.json --noEmit",
113
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.tests.json",
113
114
  "lint": "biome lint .",
114
- "check": "tsc -p tsconfig.json --noEmit",
115
+ "check": "svelte-check --tsconfig ./tsconfig.svelte-check.json",
115
116
  "test": "bun test",
116
117
  "check-i18n": "bun run src/i18n/scripts/check-coverage.ts",
117
118
  "scan-hardcoded": "bun run src/i18n/scripts/scan-hardcoded.ts"
@@ -119,13 +120,14 @@
119
120
  "devDependencies": {
120
121
  "@biomejs/biome": "^2.5.15",
121
122
  "@happy-dom/global-registrator": "^20.14.5",
122
- "@pie-element/shared-math-rendering-mathjax": "0.1.2",
123
+ "@pie-element/shared-math-rendering-mathjax": "0.1.4",
123
124
  "@playwright/test": "^1.63.0",
124
125
  "@types/semver": "^7.8.0",
125
126
  "es-module-shims": "^2.8.4",
126
127
  "esbuild": "^0.28.2",
127
128
  "glob": "^13.0.0",
128
129
  "svelte": "^5.57.1",
130
+ "svelte-check": "^4.7.6",
129
131
  "typescript": "^5.9.3"
130
132
  },
131
133
  "homepage": "https://github.com/pie-framework/pie-players/tree/master/packages/players-shared#readme",
@@ -1,17 +0,0 @@
1
- /**
2
- * Forwards the MathJax version-conflict event to instrumentation.
3
- *
4
- * The MathJax 4 adapter that browser ESM elements render with dispatches
5
- * `pie-mathjax-version-conflict` on `window`, once per condition per page, when
6
- * MathJax 3 runs on the same page — an unsupported page, see
7
- * docs/item-player/loading-strategies.md#one-mathjax-version-per-page. The
8
- * event name is repeated here because players do not depend on the element
9
- * packages.
10
- */
11
- import type { InstrumentationProvider } from "../instrumentation/types.js";
12
- export declare const MATHJAX_VERSION_CONFLICT_EVENT = "pie-mathjax-version-conflict";
13
- /**
14
- * Tracks each `pie-mathjax-version-conflict` event on `target` with the
15
- * provider `getProvider` returns when it fires, once per provider.
16
- */
17
- export declare function forwardMathjaxVersionConflicts(target: EventTarget, getProvider: () => InstrumentationProvider | undefined): void;
@@ -1,51 +0,0 @@
1
- /**
2
- * Forwards the MathJax version-conflict event to instrumentation.
3
- *
4
- * The MathJax 4 adapter that browser ESM elements render with dispatches
5
- * `pie-mathjax-version-conflict` on `window`, once per condition per page, when
6
- * MathJax 3 runs on the same page — an unsupported page, see
7
- * docs/item-player/loading-strategies.md#one-mathjax-version-per-page. The
8
- * event name is repeated here because players do not depend on the element
9
- * packages.
10
- */
11
- export const MATHJAX_VERSION_CONFLICT_EVENT = "pie-mathjax-version-conflict";
12
- // Every ESM backend on a page listens, and each player bundle carries its own
13
- // copy of this module, so the providers that tracked an event are kept on the
14
- // page.
15
- const TRACKED = Symbol.for("@pie-players/pie-players-shared/mathjax-version-conflict-tracked");
16
- function providersThatTracked(event) {
17
- const registry = globalThis;
18
- registry[TRACKED] ??= new WeakMap();
19
- let providers = registry[TRACKED].get(event);
20
- if (!providers) {
21
- providers = new WeakSet();
22
- registry[TRACKED].set(event, providers);
23
- }
24
- return providers;
25
- }
26
- /**
27
- * Tracks each `pie-mathjax-version-conflict` event on `target` with the
28
- * provider `getProvider` returns when it fires, once per provider.
29
- */
30
- export function forwardMathjaxVersionConflicts(target, getProvider) {
31
- target.addEventListener(MATHJAX_VERSION_CONFLICT_EVENT, (event) => {
32
- const provider = getProvider();
33
- if (!provider)
34
- return;
35
- const tracked = providersThatTracked(event);
36
- if (tracked.has(provider))
37
- return;
38
- tracked.add(provider);
39
- const detail = event.detail;
40
- const { condition, docsUrl } = (detail && typeof detail === "object" ? detail : {});
41
- try {
42
- provider.trackEvent(MATHJAX_VERSION_CONFLICT_EVENT, {
43
- condition,
44
- docsUrl,
45
- });
46
- }
47
- catch {
48
- // Swallow: instrumentation must never break rendering.
49
- }
50
- });
51
- }
@@ -1,50 +0,0 @@
1
- import { type Stage, type StageChangeDetail, type StageSourceCe, type StageStatus } from "./stages.js";
2
- /**
3
- * Stage tracker primitive (M6).
4
- *
5
- * One tracker per CE instance per cohort. `enter(stage, status?)` is the
6
- * only mutation; `reset()` rolls the cohort over (e.g. when
7
- * `(sectionId, attemptId)` changes).
8
- *
9
- * The tracker enforces the locked invariants from
10
- * `m6_ready_vocab_canonical_followup.plan.md` §3.1–§3.6:
11
- * - Monotonic ordering across `STAGES`. Backward transitions inside one
12
- * cohort are rejected (dev) / warned-once (prod) so subscribers never
13
- * see a stage regress without an intervening `reset()`.
14
- * - Per-CE applicability. The post-retro canonical list applies to
15
- * every CE shape; the `applicableStages` indirection is kept so a
16
- * future shape-specific stage can opt out without rewriting callers.
17
- * Non-applicable stages auto-emit `status: "skipped"` when an
18
- * applicable later stage is entered, so iteration order stays stable
19
- * across CE shapes.
20
- * - One source for every emit. The injected `emit(detail)` function
21
- * drives DOM events, engine subscribers, and coordinator subscribers
22
- * from the same call site so no surface drifts.
23
- *
24
- * Failure semantics intentionally piggyback on the M3 framework-error
25
- * contract: `enter(stage, "failed")` records the position; the
26
- * framework error records the cause.
27
- */
28
- export type CreateStageTrackerOptions = {
29
- sourceCe: string;
30
- sourceCeShape: StageSourceCe;
31
- runtimeId: string;
32
- sectionId?: string;
33
- attemptId?: string;
34
- emit: (detail: StageChangeDetail) => void;
35
- now?: () => string;
36
- onUnexpectedTransition?: (info: {
37
- from: Stage | null;
38
- to: Stage;
39
- reason: "backward" | "duplicate";
40
- }) => void;
41
- };
42
- export type StageTracker = {
43
- enter: (stage: Stage, status?: StageStatus) => void;
44
- reset: (next?: {
45
- sectionId?: string;
46
- attemptId?: string;
47
- }) => void;
48
- getCurrent: () => Stage | null;
49
- };
50
- export declare function createStageTracker(opts: CreateStageTrackerOptions): StageTracker;
@@ -1,105 +0,0 @@
1
- import { STAGES, applicableStages, stageOrdinal, } from "./stages.js";
2
- let unexpectedTransitionWarned = false;
3
- function defaultUnexpectedTransitionLogger(info) {
4
- if (unexpectedTransitionWarned)
5
- return;
6
- unexpectedTransitionWarned = true;
7
- if (typeof console !== "undefined" && console.warn) {
8
- console.warn(`[pie-stage-tracker] Unexpected ${info.reason} stage transition ` +
9
- `from \`${info.from ?? "<none>"}\` to \`${info.to}\`. ` +
10
- `Stage transitions are expected to be monotonic within a cohort; ` +
11
- `call \`reset()\` when \`(sectionId, attemptId)\` changes.`);
12
- }
13
- }
14
- export function createStageTracker(opts) {
15
- const applicable = applicableStages(opts.sourceCeShape);
16
- const applicableSet = new Set(applicable);
17
- const now = opts.now ?? (() => new Date().toISOString());
18
- const onUnexpected = opts.onUnexpectedTransition ?? defaultUnexpectedTransitionLogger;
19
- let current = null;
20
- let sectionId = opts.sectionId;
21
- let attemptId = opts.attemptId;
22
- const emitted = new Set();
23
- function emitDetail(stage, status) {
24
- opts.emit({
25
- stage,
26
- status,
27
- runtimeId: opts.runtimeId,
28
- sectionId,
29
- attemptId,
30
- timestamp: now(),
31
- sourceCe: opts.sourceCe,
32
- });
33
- }
34
- function fillSkippedBefore(stage) {
35
- const targetIdx = stageOrdinal(stage);
36
- const startIdx = current === null ? 0 : stageOrdinal(current) + 1;
37
- for (let idx = startIdx; idx < targetIdx; idx += 1) {
38
- const skipCandidate = STAGES[idx];
39
- if (applicableSet.has(skipCandidate))
40
- continue;
41
- if (emitted.has(skipCandidate))
42
- continue;
43
- emitted.add(skipCandidate);
44
- emitDetail(skipCandidate, "skipped");
45
- }
46
- }
47
- function enter(stage, status = "entered") {
48
- const nextOrdinal = stageOrdinal(stage);
49
- const currentOrdinal = current === null ? -1 : stageOrdinal(current);
50
- if (status === "entered") {
51
- if (emitted.has(stage)) {
52
- onUnexpected({ from: current, to: stage, reason: "duplicate" });
53
- return;
54
- }
55
- if (nextOrdinal < currentOrdinal) {
56
- onUnexpected({ from: current, to: stage, reason: "backward" });
57
- return;
58
- }
59
- if (!applicableSet.has(stage)) {
60
- // A non-applicable stage entered with `entered` is a programming
61
- // error: the tracker auto-skips it when a later stage enters.
62
- // Treat it as a `skipped` emission and continue.
63
- if (emitted.has(stage))
64
- return;
65
- emitted.add(stage);
66
- emitDetail(stage, "skipped");
67
- return;
68
- }
69
- fillSkippedBefore(stage);
70
- emitted.add(stage);
71
- current = stage;
72
- emitDetail(stage, "entered");
73
- return;
74
- }
75
- // `status === "failed"` records position; do not gate on monotonic
76
- // ordering — failure can happen during any transition. Mark the stage
77
- // as emitted so a subsequent `entered` for the same stage warns as a
78
- // duplicate; downstream recovery should call `reset()` first.
79
- if (status === "failed") {
80
- fillSkippedBefore(stage);
81
- emitted.add(stage);
82
- current = stage;
83
- emitDetail(stage, "failed");
84
- return;
85
- }
86
- // `status === "skipped"` is reserved for the auto-fill path; explicit
87
- // callers shouldn't pass it. Accept it idempotently for robustness.
88
- if (emitted.has(stage))
89
- return;
90
- emitted.add(stage);
91
- emitDetail(stage, "skipped");
92
- }
93
- function reset(next) {
94
- current = null;
95
- emitted.clear();
96
- if (next) {
97
- sectionId = next.sectionId;
98
- attemptId = next.attemptId;
99
- }
100
- }
101
- function getCurrent() {
102
- return current;
103
- }
104
- return { enter, reset, getCurrent };
105
- }