@pie-players/pie-players-shared 0.3.61 → 0.3.63

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 (147) hide show
  1. package/dist/config/profile.js +0 -1
  2. package/dist/i18n/index.js +0 -1
  3. package/dist/i18n/loader.js +0 -1
  4. package/dist/i18n/simple-i18n.js +0 -1
  5. package/dist/i18n/types.js +0 -1
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +1 -1
  8. package/dist/instrumentation/debug-panel-stream.js +0 -1
  9. package/dist/instrumentation/index.js +0 -1
  10. package/dist/instrumentation/provider-guards.js +0 -1
  11. package/dist/instrumentation/providers/BaseInstrumentationProvider.js +0 -1
  12. package/dist/instrumentation/providers/CompositeInstrumentationProvider.js +0 -1
  13. package/dist/instrumentation/providers/ConsoleInstrumentationProvider.js +0 -1
  14. package/dist/instrumentation/providers/DebugPanelInstrumentationProvider.js +0 -1
  15. package/dist/instrumentation/providers/NewRelicInstrumentationProvider.js +0 -1
  16. package/dist/instrumentation/providers/index.js +0 -1
  17. package/dist/instrumentation/types.js +0 -1
  18. package/dist/loader-config.js +0 -1
  19. package/dist/loaders/ElementLoader.js +0 -1
  20. package/dist/loaders/element-loader-types.js +0 -1
  21. package/dist/loaders/element-loader.js +0 -1
  22. package/dist/loaders/element-package-policy.js +0 -1
  23. package/dist/loaders/esm-adapter.js +0 -1
  24. package/dist/loaders/iife-adapter.js +0 -1
  25. package/dist/loaders/index.js +0 -1
  26. package/dist/object/index.js +0 -1
  27. package/dist/pie/asset-handler.js +0 -1
  28. package/dist/pie/authoring-tag.js +0 -1
  29. package/dist/pie/authoring.js +0 -1
  30. package/dist/pie/component-context.js +0 -1
  31. package/dist/pie/config.js +0 -1
  32. package/dist/pie/configure-initialization.js +0 -1
  33. package/dist/pie/correct-response-env.js +0 -1
  34. package/dist/pie/custom-element-define.js +0 -1
  35. package/dist/pie/index.js +0 -1
  36. package/dist/pie/initialization.js +0 -1
  37. package/dist/pie/instrumentation-event-bridge.js +0 -1
  38. package/dist/pie/instrumentation-event-map.js +0 -1
  39. package/dist/pie/instrumentation-provider-resolution.js +0 -1
  40. package/dist/pie/item-controller-storage.js +0 -1
  41. package/dist/pie/item-controller.js +0 -1
  42. package/dist/pie/item-session-contract.js +0 -1
  43. package/dist/pie/logger.js +0 -1
  44. package/dist/pie/math-rendering.js +0 -1
  45. package/dist/pie/overrides.js +0 -1
  46. package/dist/pie/player-initializer.js +0 -1
  47. package/dist/pie/registry.js +0 -1
  48. package/dist/pie/resource-monitor.js +0 -1
  49. package/dist/pie/scoring.js +0 -1
  50. package/dist/pie/stage-tracker.js +0 -1
  51. package/dist/pie/stages.js +0 -1
  52. package/dist/pie/tag-names.js +0 -1
  53. package/dist/pie/types.js +0 -1
  54. package/dist/pie/updates.js +0 -1
  55. package/dist/pie/utils.js +0 -1
  56. package/dist/pie/versioned-tag.js +0 -1
  57. package/dist/player-strategy.js +0 -1
  58. package/dist/security/index.js +0 -1
  59. package/dist/security/sanitize-item-markup.d.ts +11 -0
  60. package/dist/security/sanitize-item-markup.js +5 -3
  61. package/dist/security/sanitize-svg-icon.js +0 -1
  62. package/dist/security/validate-style-url.js +0 -1
  63. package/dist/security/wrap-model-rich-content.js +0 -1
  64. package/dist/security/wrap-overwide-images.js +0 -1
  65. package/dist/security/wrap-overwide-tables.js +0 -1
  66. package/dist/server/npm-registry.js +0 -1
  67. package/dist/types/index.js +0 -1
  68. package/dist/ui/content-styles.js +0 -1
  69. package/dist/ui/debug-panel-persistence.js +0 -1
  70. package/dist/ui/first-focusable.js +0 -1
  71. package/dist/ui/focus-trap.js +0 -1
  72. package/dist/ui/safe-storage.js +0 -1
  73. package/dist/ui/scope-css.d.ts +56 -0
  74. package/dist/ui/scope-css.js +310 -0
  75. package/dist/ui/zoom-compensation.js +0 -1
  76. package/package.json +4 -4
  77. package/dist/config/profile.js.map +0 -1
  78. package/dist/i18n/index.js.map +0 -1
  79. package/dist/i18n/loader.js.map +0 -1
  80. package/dist/i18n/simple-i18n.js.map +0 -1
  81. package/dist/i18n/types.js.map +0 -1
  82. package/dist/index.js.map +0 -1
  83. package/dist/instrumentation/debug-panel-stream.js.map +0 -1
  84. package/dist/instrumentation/index.js.map +0 -1
  85. package/dist/instrumentation/provider-guards.js.map +0 -1
  86. package/dist/instrumentation/providers/BaseInstrumentationProvider.js.map +0 -1
  87. package/dist/instrumentation/providers/CompositeInstrumentationProvider.js.map +0 -1
  88. package/dist/instrumentation/providers/ConsoleInstrumentationProvider.js.map +0 -1
  89. package/dist/instrumentation/providers/DebugPanelInstrumentationProvider.js.map +0 -1
  90. package/dist/instrumentation/providers/NewRelicInstrumentationProvider.js.map +0 -1
  91. package/dist/instrumentation/providers/index.js.map +0 -1
  92. package/dist/instrumentation/types.js.map +0 -1
  93. package/dist/loader-config.js.map +0 -1
  94. package/dist/loaders/ElementLoader.js.map +0 -1
  95. package/dist/loaders/element-loader-types.js.map +0 -1
  96. package/dist/loaders/element-loader.js.map +0 -1
  97. package/dist/loaders/element-package-policy.js.map +0 -1
  98. package/dist/loaders/esm-adapter.js.map +0 -1
  99. package/dist/loaders/iife-adapter.js.map +0 -1
  100. package/dist/loaders/index.js.map +0 -1
  101. package/dist/object/index.js.map +0 -1
  102. package/dist/pie/asset-handler.js.map +0 -1
  103. package/dist/pie/authoring-tag.js.map +0 -1
  104. package/dist/pie/authoring.js.map +0 -1
  105. package/dist/pie/component-context.js.map +0 -1
  106. package/dist/pie/config.js.map +0 -1
  107. package/dist/pie/configure-initialization.js.map +0 -1
  108. package/dist/pie/correct-response-env.js.map +0 -1
  109. package/dist/pie/custom-element-define.js.map +0 -1
  110. package/dist/pie/index.js.map +0 -1
  111. package/dist/pie/initialization.js.map +0 -1
  112. package/dist/pie/instrumentation-event-bridge.js.map +0 -1
  113. package/dist/pie/instrumentation-event-map.js.map +0 -1
  114. package/dist/pie/instrumentation-provider-resolution.js.map +0 -1
  115. package/dist/pie/item-controller-storage.js.map +0 -1
  116. package/dist/pie/item-controller.js.map +0 -1
  117. package/dist/pie/item-session-contract.js.map +0 -1
  118. package/dist/pie/logger.js.map +0 -1
  119. package/dist/pie/math-rendering.js.map +0 -1
  120. package/dist/pie/overrides.js.map +0 -1
  121. package/dist/pie/player-initializer.js.map +0 -1
  122. package/dist/pie/registry.js.map +0 -1
  123. package/dist/pie/resource-monitor.js.map +0 -1
  124. package/dist/pie/scoring.js.map +0 -1
  125. package/dist/pie/stage-tracker.js.map +0 -1
  126. package/dist/pie/stages.js.map +0 -1
  127. package/dist/pie/tag-names.js.map +0 -1
  128. package/dist/pie/types.js.map +0 -1
  129. package/dist/pie/updates.js.map +0 -1
  130. package/dist/pie/utils.js.map +0 -1
  131. package/dist/pie/versioned-tag.js.map +0 -1
  132. package/dist/player-strategy.js.map +0 -1
  133. package/dist/security/index.js.map +0 -1
  134. package/dist/security/sanitize-item-markup.js.map +0 -1
  135. package/dist/security/sanitize-svg-icon.js.map +0 -1
  136. package/dist/security/validate-style-url.js.map +0 -1
  137. package/dist/security/wrap-model-rich-content.js.map +0 -1
  138. package/dist/security/wrap-overwide-images.js.map +0 -1
  139. package/dist/security/wrap-overwide-tables.js.map +0 -1
  140. package/dist/server/npm-registry.js.map +0 -1
  141. package/dist/types/index.js.map +0 -1
  142. package/dist/ui/content-styles.js.map +0 -1
  143. package/dist/ui/debug-panel-persistence.js.map +0 -1
  144. package/dist/ui/first-focusable.js.map +0 -1
  145. package/dist/ui/focus-trap.js.map +0 -1
  146. package/dist/ui/safe-storage.js.map +0 -1
  147. package/dist/ui/zoom-compensation.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"scoring.js","sourceRoot":"","sources":["../../src/pie/scoring.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * PIE Scoring Module\n *\n * Scoring and outcome calculation for PIE elements.\n */\n\nimport type {\n\tConfigEntity,\n\tEnv,\n\tOutcomeResponse,\n\tPieController,\n\tPieModel,\n} from \"../types/index.js\";\nimport { createPieLogger, isGlobalDebugEnabled } from \"./logger.js\";\nimport { pieRegistry } from \"./registry.js\";\nimport type { PieElement } from \"./types.js\";\nimport { BundleType } from \"./types.js\";\nimport { findOrAddSession } from \"./utils.js\";\n\n// Create module-level logger (respects global debug flag - pass function for dynamic checking)\nconst logger = createPieLogger(\"pie-scoring\", () => isGlobalDebugEnabled());\n\n/**\n * Find the controller for a PIE element\n */\nexport const findPieController = (\n\telementName: string,\n): PieController | undefined => {\n\tconst registry = pieRegistry();\n\n\tlogger.debug(\n\t\t`[findPieController] Looking for controller for: ${elementName}`,\n\t);\n\tlogger.debug(\n\t\t`[findPieController] Registry has entries for:`,\n\t\tObject.keys(registry),\n\t);\n\n\tconst entry = registry[elementName];\n\tif (!entry) {\n\t\tlogger.error(\n\t\t\t`[findPieController] ❌ No registry entry found for element: ${elementName}`,\n\t\t);\n\t\tlogger.error(\n\t\t\t`[findPieController] Available element names:`,\n\t\t\tObject.keys(registry),\n\t\t);\n\t\tlogger.error(`[findPieController] Full registry:`, registry);\n\t\treturn undefined;\n\t}\n\n\tlogger.debug(`[findPieController] Found registry entry for ${elementName}:`, {\n\t\tpackage: entry.package,\n\t\ttagName: entry.tagName,\n\t\thasController: !!entry.controller,\n\t\tcontrollerKeys: entry.controller ? Object.keys(entry.controller) : [],\n\t\tbundleType: entry.bundleType,\n\t});\n\n\tconst controller = entry.controller;\n\tif (!controller) {\n\t\t// Check if missing controller is expected based on bundle type\n\t\tif (entry.bundleType === BundleType.clientPlayer) {\n\t\t\t// client-player.js MUST have controllers\n\t\t\tlogger.error(\n\t\t\t\t`[findPieController] ❌ CRITICAL: Registry entry exists for ${elementName} but controller is missing!`,\n\t\t\t);\n\t\t\tlogger.error(\n\t\t\t\t`[findPieController] Bundle type: ${entry.bundleType} (controllers required)`,\n\t\t\t);\n\t\t\tlogger.error(`[findPieController] Entry:`, entry);\n\t\t\tthrow new Error(\n\t\t\t\t`No controller found for ${elementName}. client-player.js bundles MUST include controllers. Check bundle loading and registration.`,\n\t\t\t);\n\t\t} else {\n\t\t\t// player.js doesn't have controllers - this is expected\n\t\t\tlogger.debug(\n\t\t\t\t`[findPieController] ℹ️ No controller for ${elementName} - using server-processed models (player.js bundle)`,\n\t\t\t);\n\t\t}\n\t} else {\n\t\tlogger.debug(\n\t\t\t`[findPieController] ✅ Controller found for ${elementName} with functions:`,\n\t\t\tObject.keys(controller),\n\t\t);\n\t}\n\n\treturn controller;\n};\n\nexport type ScorePieItemOptions = {\n\t/** Scope element lookup to a specific player/root. Defaults to document for existing callers. */\n\tcontainer?: Pick<ParentNode, \"querySelector\">;\n\tenv?: Partial<Env>;\n\tmodels?: PieModel[];\n\t/**\n\t * Existing callers keep the historical scorePieItem controller call shape.\n\t * provideScore() opts into the item-player score result shape explicitly.\n\t */\n\toutcomeArguments?: \"session-env\" | \"model-session-env\";\n\t/**\n\t * Preserve one result slot per model, including undefined for missing element/controller.\n\t * Existing scorePieItem callers keep the filtered result shape by default.\n\t */\n\tincludeMissingResults?: boolean;\n};\n\nconst escapeAttributeSelectorValue = (value: string): string =>\n\tvalue\n\t\t.replace(/\\\\/g, \"\\\\\\\\\")\n\t\t.replace(/\"/g, '\\\\\"')\n\t\t.replace(/\\n/g, \"\\\\a \")\n\t\t.replace(/\\r/g, \"\\\\d \");\n\nconst findRenderedPieElement = (\n\troot: Pick<ParentNode, \"querySelector\">,\n\tmodelId: string,\n): PieElement | null => {\n\tconst escapedId = escapeAttributeSelectorValue(modelId);\n\treturn (root.querySelector(`[id=\"${escapedId}\"]`) ||\n\t\troot.querySelector(`[pie-id=\"${escapedId}\"]`)) as PieElement | null;\n};\n\nconst invokeOutcome = async (\n\tcontroller: PieController,\n\tmodel: PieModel,\n\tsession: any,\n\tenv: Partial<Env>,\n\targumentShape: \"session-env\" | \"model-session-env\",\n): Promise<OutcomeResponse> => {\n\tif (argumentShape === \"model-session-env\") {\n\t\tconst outcome = controller.outcome as unknown as (\n\t\t\tmodel: PieModel,\n\t\t\tsession: any,\n\t\t\tenv: Partial<Env>,\n\t\t) => Promise<OutcomeResponse>;\n\t\treturn outcome.call(controller, model, session, env);\n\t}\n\tconst outcome = controller.outcome as unknown as (\n\t\tsession: any,\n\t\tenv: Partial<Env>,\n\t) => Promise<OutcomeResponse>;\n\treturn outcome.call(controller, session, env);\n};\n\nexport function scorePieItem(\n\tconfig: ConfigEntity,\n\tsessionData: any[],\n\toptions: ScorePieItemOptions & { includeMissingResults: true },\n): Promise<{ results: Array<OutcomeResponse | undefined> }>;\nexport function scorePieItem(\n\tconfig: ConfigEntity,\n\tsessionData: any[],\n\toptions?: ScorePieItemOptions,\n): Promise<{ results: OutcomeResponse[] }>;\n/**\n * Score a PIE item by calling outcome on all PIE elements\n */\nexport async function scorePieItem(\n\tconfig: ConfigEntity,\n\tsessionData: any[],\n\toptions: ScorePieItemOptions = {},\n): Promise<{ results: Array<OutcomeResponse | undefined> }> {\n\tconst root = options.container ?? document;\n\tconst models = options.models ?? config.models ?? [];\n\tconst outcomeArguments = options.outcomeArguments ?? \"session-env\";\n\tconst scoringEnv: Partial<Env> = {\n\t\t...options.env,\n\t\tmode: \"evaluate\",\n\t\tpartialScoring: options.env?.partialScoring,\n\t};\n\tconst r = await Promise.all(\n\t\tmodels.map(async (model) => {\n\t\t\tconst pieEl = findRenderedPieElement(root, model.id);\n\t\t\tlogger.debug(\"found pieEl %O for model id %s\", pieEl, model.id);\n\t\t\tconst session = findOrAddSession(sessionData, model.id, model.element);\n\t\t\tif (pieEl) {\n\t\t\t\tconst controller = findPieController(pieEl.localName);\n\t\t\t\tif (controller?.outcome) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...session,\n\t\t\t\t\t\t...(await invokeOutcome(\n\t\t\t\t\t\t\tcontroller,\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\tsession,\n\t\t\t\t\t\t\tscoringEnv,\n\t\t\t\t\t\t\toutcomeArguments,\n\t\t\t\t\t\t)),\n\t\t\t\t\t};\n\t\t\t\t} else {\n\t\t\t\t\tlogger.debug(\"ignore %s (no controller)\", pieEl.localName);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlogger.debug(\"ignore %s (no element found for %s)\", model.id);\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}),\n\t);\n\n\tlogger.debug(\"scorePieItem result\", r);\n\treturn {\n\t\tresults: options.includeMissingResults\n\t\t\t? r\n\t\t\t: (r.filter(Boolean) as OutcomeResponse[]),\n\t};\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"stage-tracker.js","sourceRoot":"","sources":["../../src/pie/stage-tracker.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tSTAGES,\n\tapplicableStages,\n\tstageOrdinal,\n\ttype Stage,\n\ttype StageChangeDetail,\n\ttype StageSourceCe,\n\ttype StageStatus,\n} from \"./stages.js\";\n\n/**\n * Stage tracker primitive (M6).\n *\n * One tracker per CE instance per cohort. `enter(stage, status?)` is the\n * only mutation; `reset()` rolls the cohort over (e.g. when\n * `(sectionId, attemptId)` changes).\n *\n * The tracker enforces the locked invariants from\n * `m6_ready_vocab_canonical_followup.plan.md` §3.1–§3.6:\n * - Monotonic ordering across `STAGES`. Backward transitions inside one\n * cohort are rejected (dev) / warned-once (prod) so subscribers never\n * see a stage regress without an intervening `reset()`.\n * - Per-CE applicability. The post-retro canonical list applies to\n * every CE shape; the `applicableStages` indirection is kept so a\n * future shape-specific stage can opt out without rewriting callers.\n * Non-applicable stages auto-emit `status: \"skipped\"` when an\n * applicable later stage is entered, so iteration order stays stable\n * across CE shapes.\n * - One source for every emit. The injected `emit(detail)` function\n * drives DOM events, engine subscribers, and coordinator subscribers\n * from the same call site so no surface drifts.\n *\n * Failure semantics intentionally piggyback on the M3 framework-error\n * contract: `enter(stage, \"failed\")` records the position; the\n * framework error records the cause.\n */\n\nexport type CreateStageTrackerOptions = {\n\tsourceCe: string;\n\tsourceCeShape: StageSourceCe;\n\truntimeId: string;\n\tsectionId?: string;\n\tattemptId?: string;\n\temit: (detail: StageChangeDetail) => void;\n\tnow?: () => string;\n\tonUnexpectedTransition?: (info: {\n\t\tfrom: Stage | null;\n\t\tto: Stage;\n\t\treason: \"backward\" | \"duplicate\";\n\t}) => void;\n};\n\nexport type StageTracker = {\n\tenter: (stage: Stage, status?: StageStatus) => void;\n\treset: (next?: { sectionId?: string; attemptId?: string }) => void;\n\tgetCurrent: () => Stage | null;\n};\n\nlet unexpectedTransitionWarned = false;\n\nfunction defaultUnexpectedTransitionLogger(info: {\n\tfrom: Stage | null;\n\tto: Stage;\n\treason: \"backward\" | \"duplicate\";\n}): void {\n\tif (unexpectedTransitionWarned) return;\n\tunexpectedTransitionWarned = true;\n\tif (typeof console !== \"undefined\" && console.warn) {\n\t\tconsole.warn(\n\t\t\t`[pie-stage-tracker] Unexpected ${info.reason} stage transition ` +\n\t\t\t\t`from \\`${info.from ?? \"<none>\"}\\` to \\`${info.to}\\`. ` +\n\t\t\t\t`Stage transitions are expected to be monotonic within a cohort; ` +\n\t\t\t\t`call \\`reset()\\` when \\`(sectionId, attemptId)\\` changes.`,\n\t\t);\n\t}\n}\n\nexport function createStageTracker(\n\topts: CreateStageTrackerOptions,\n): StageTracker {\n\tconst applicable = applicableStages(opts.sourceCeShape);\n\tconst applicableSet = new Set(applicable);\n\tconst now = opts.now ?? (() => new Date().toISOString());\n\tconst onUnexpected =\n\t\topts.onUnexpectedTransition ?? defaultUnexpectedTransitionLogger;\n\n\tlet current: Stage | null = null;\n\tlet sectionId = opts.sectionId;\n\tlet attemptId = opts.attemptId;\n\tconst emitted = new Set<Stage>();\n\n\tfunction emitDetail(stage: Stage, status: StageStatus): void {\n\t\topts.emit({\n\t\t\tstage,\n\t\t\tstatus,\n\t\t\truntimeId: opts.runtimeId,\n\t\t\tsectionId,\n\t\t\tattemptId,\n\t\t\ttimestamp: now(),\n\t\t\tsourceCe: opts.sourceCe,\n\t\t});\n\t}\n\n\tfunction fillSkippedBefore(stage: Stage): void {\n\t\tconst targetIdx = stageOrdinal(stage);\n\t\tconst startIdx = current === null ? 0 : stageOrdinal(current) + 1;\n\t\tfor (let idx = startIdx; idx < targetIdx; idx += 1) {\n\t\t\tconst skipCandidate = STAGES[idx];\n\t\t\tif (applicableSet.has(skipCandidate)) continue;\n\t\t\tif (emitted.has(skipCandidate)) continue;\n\t\t\temitted.add(skipCandidate);\n\t\t\temitDetail(skipCandidate, \"skipped\");\n\t\t}\n\t}\n\n\tfunction enter(stage: Stage, status: StageStatus = \"entered\"): void {\n\t\tconst nextOrdinal = stageOrdinal(stage);\n\t\tconst currentOrdinal = current === null ? -1 : stageOrdinal(current);\n\n\t\tif (status === \"entered\") {\n\t\t\tif (emitted.has(stage)) {\n\t\t\t\tonUnexpected({ from: current, to: stage, reason: \"duplicate\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (nextOrdinal < currentOrdinal) {\n\t\t\t\tonUnexpected({ from: current, to: stage, reason: \"backward\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!applicableSet.has(stage)) {\n\t\t\t\t// A non-applicable stage entered with `entered` is a programming\n\t\t\t\t// error: the tracker auto-skips it when a later stage enters.\n\t\t\t\t// Treat it as a `skipped` emission and continue.\n\t\t\t\tif (emitted.has(stage)) return;\n\t\t\t\temitted.add(stage);\n\t\t\t\temitDetail(stage, \"skipped\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tfillSkippedBefore(stage);\n\t\t\temitted.add(stage);\n\t\t\tcurrent = stage;\n\t\t\temitDetail(stage, \"entered\");\n\t\t\treturn;\n\t\t}\n\n\t\t// `status === \"failed\"` records position; do not gate on monotonic\n\t\t// ordering — failure can happen during any transition. Mark the stage\n\t\t// as emitted so a subsequent `entered` for the same stage warns as a\n\t\t// duplicate; downstream recovery should call `reset()` first.\n\t\tif (status === \"failed\") {\n\t\t\tfillSkippedBefore(stage);\n\t\t\temitted.add(stage);\n\t\t\tcurrent = stage;\n\t\t\temitDetail(stage, \"failed\");\n\t\t\treturn;\n\t\t}\n\n\t\t// `status === \"skipped\"` is reserved for the auto-fill path; explicit\n\t\t// callers shouldn't pass it. Accept it idempotently for robustness.\n\t\tif (emitted.has(stage)) return;\n\t\temitted.add(stage);\n\t\temitDetail(stage, \"skipped\");\n\t}\n\n\tfunction reset(next?: { sectionId?: string; attemptId?: string }): void {\n\t\tcurrent = null;\n\t\temitted.clear();\n\t\tif (next) {\n\t\t\tsectionId = next.sectionId;\n\t\t\tattemptId = next.attemptId;\n\t\t}\n\t}\n\n\tfunction getCurrent(): Stage | null {\n\t\treturn current;\n\t}\n\n\treturn { enter, reset, getCurrent };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"stages.js","sourceRoot":"","sources":["../../src/pie/stages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IACnC,UAAU;IACV,cAAc;IACd,aAAa;IACb,UAAU;CACD,CAAC,CAAC;AAcZ;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,SAAwB;IACxD,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,KAAY;IACxC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QAChB,MAAM,IAAI,KAAK,CAAC,mBAAmB,KAAK,IAAI,CAAC,CAAC;IAC/C,CAAC;IACD,OAAO,GAAG,CAAC;AACZ,CAAC","sourcesContent":["/**\n * Canonical stage vocabulary (M6 of the Coherent Options Surface track).\n *\n * One monotonic stage list is the unified replacement for the previous\n * drift of `toolkit-ready`, `section-ready`, `section-controller-ready`,\n * `interaction-ready`, `readiness-change`, and `ready` — all of which\n * have been removed. Hosts ask the same question — \"is this CE at\n * stage ≥ X?\" — across every `<pie-section-player-*>` and\n * `<pie-assessment-toolkit>` element.\n *\n * The four-stage canonical list (`composed`, `engine-ready`, `interactive`,\n * `disposed`) was finalized after the M5/M6 cumulative review confirmed\n * zero internal or external consumers for the original `attached`,\n * `runtime-bound`, and `ui-rendered` stages. The retro keeps the same\n * shape for both the toolkit CE and the layout CEs — every CE iterates\n * through the same list so subscribers don't need to special-case shape.\n *\n * Locked decisions (see `m6_ready_vocab_canonical_followup.plan.md`):\n * - **A1** Stage machine model with one canonical event family.\n * - **B1** \"All items loaded\" is reclassified as `pie-loading-complete`,\n * not a stage. (The previous `ready` DOM event alias was removed in\n * the broad architecture review compat sweep.)\n * - **C1** `readiness.mode = \"strict\"` only delays the `interactive`\n * transition; every earlier stage fires identically across modes.\n * - **D1** One DOM event family `pie-stage-change` with the stage in the\n * payload, mirroring the M3 `framework-error` idiom.\n *\n * Failure semantics deliberately stay in the M3 framework-error contract —\n * `status: \"failed\"` records the position; the framework error records the\n * cause.\n */\n\nexport const STAGES = Object.freeze([\n\t\"composed\",\n\t\"engine-ready\",\n\t\"interactive\",\n\t\"disposed\",\n] as const);\n\nexport type Stage = (typeof STAGES)[number];\n\nexport type StageStatus = \"entered\" | \"skipped\" | \"failed\";\n\n/**\n * Subset of the source CE shape used by stage subscribers. Both shapes\n * apply every canonical stage post-retro — the distinction stays in the\n * type so future shape-specific stages (e.g. layout-only `paginated`) can\n * be added without churn at the call sites.\n */\nexport type StageSourceCe = \"toolkit\" | \"layout\";\n\n/**\n * Stages that apply to a given CE shape. Post-retro the toolkit and the\n * layout CEs share the same canonical list; `applicableStages` is kept as\n * an indirection so the tracker, telemetry, and tests don't have to be\n * rewritten when a future stage diverges between shapes.\n */\nexport function applicableStages(_sourceCe: StageSourceCe): readonly Stage[] {\n\treturn STAGES;\n}\n\n/**\n * Strict ordering index used by the tracker to detect monotonic\n * violations. `disposed` is last; `composed` is first.\n */\nexport function stageOrdinal(stage: Stage): number {\n\tconst idx = STAGES.indexOf(stage);\n\tif (idx === -1) {\n\t\tthrow new Error(`Unknown stage \\`${stage}\\``);\n\t}\n\treturn idx;\n}\n\nexport type StageChangeDetail = {\n\tstage: Stage;\n\tstatus: StageStatus;\n\t/** Matches the existing `runtimeId` carried by toolkit telemetry. */\n\truntimeId: string;\n\tsectionId?: string;\n\tattemptId?: string;\n\t/** ISO-8601, monotonic across one cohort. */\n\ttimestamp: string;\n\t/** Tag name minus the `--version-<encoded>` suffix. */\n\tsourceCe: string;\n};\n\nexport type LoadingCompleteDetail = {\n\truntimeId: string;\n\tsectionId: string;\n\tattemptId?: string;\n\titemCount: number;\n\tloadedCount: number;\n\ttimestamp: string;\n\tsourceCe: string;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tag-names.js","sourceRoot":"","sources":["../../src/pie/tag-names.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IAClC,gBAAgB;IAChB,eAAe;IACf,WAAW;IACX,eAAe;IACf,eAAe;IACf,kBAAkB;IAClB,gBAAgB;IAChB,eAAe;CACf,CAAC,CAAC;AAEH,MAAM,UAAU,GAAG,kCAAkC,CAAC;AAItD,MAAM,CAAC,MAAM,eAAe,GAAgC;IAC3D,QAAQ,EAAE,EAAE;IACZ,MAAM,EAAE,SAAS;IACjB,KAAK,EAAE,QAAQ;CACf,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACvC,IAAY,EACZ,OAAO,GAAG,oBAAoB,EACrB,EAAE;IACX,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,WAAW,OAAO,+BAA+B,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,WAAW,OAAO,gBAAgB,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,UAAU,KAAK,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CACd,WAAW,OAAO,KAAK,UAAU,2CAA2C,CAC5E,CAAC;IACH,CAAC;IAED,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACd,WAAW,OAAO,KAAK,UAAU,+CAA+C,CAChF,CAAC;IACH,CAAC;IAED,IAAI,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CACd,WAAW,OAAO,KAAK,UAAU,2CAA2C,CAC5E,CAAC;IACH,CAAC;IAED,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CACd,WAAW,OAAO,KAAK,UAAU,wEAAwE,CACzG,CAAC;IACH,CAAC;IAED,OAAO,UAAU,CAAC;AACnB,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACxB,OAAe,EACf,IAA0B,EAC1B,YAAqB,EACZ,EAAE;IACX,MAAM,MAAM,GACX,YAAY;QACZ,CAAC,eAAe,CAAC,IAAmB,CAAC,KAAK,SAAS;YAClD,CAAC,CAAC,eAAe,CAAC,IAAmB,CAAC;YACtC,CAAC,CAAC,EAAE,CAAC,CAAC;IACR,OAAO,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC;AAC9B,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC/B,OAAe,EACf,UAAkB,EAClB,MAAiC,EACxB,EAAE;IACX,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAC1C,OAAO,GAAG,OAAO,UAAU,IAAI,EAAE,CAAC;AACnC,CAAC,CAAC","sourcesContent":["/**\n * Custom element tag helpers for PIE runtime loaders/players.\n */\n\nconst RESERVED_TAG_NAMES = new Set([\n\t\"annotation-xml\",\n\t\"color-profile\",\n\t\"font-face\",\n\t\"font-face-src\",\n\t\"font-face-uri\",\n\t\"font-face-format\",\n\t\"font-face-name\",\n\t\"missing-glyph\",\n]);\n\nconst TAG_FORMAT = /^[a-z][a-z0-9._-]*-[a-z0-9._-]*$/;\n\nexport type PieViewMode = \"delivery\" | \"author\" | \"print\";\n\nexport const VIEW_TAG_SUFFIX: Record<PieViewMode, string> = {\n\tdelivery: \"\",\n\tauthor: \"-config\",\n\tprint: \"-print\",\n};\n\n/**\n * Validate a custom element tag name according to platform constraints.\n */\nexport const validateCustomElementTag = (\n\tname: string,\n\tcontext = \"custom element tag\",\n): string => {\n\tif (!name || typeof name !== \"string\") {\n\t\tthrow new Error(`Invalid ${context}: expected a non-empty string`);\n\t}\n\n\tconst normalized = name.trim();\n\tif (!normalized) {\n\t\tthrow new Error(`Invalid ${context}: tag is empty`);\n\t}\n\n\tif (normalized !== normalized.toLowerCase()) {\n\t\tthrow new Error(\n\t\t\t`Invalid ${context} \"${normalized}\": custom element names must be lowercase`,\n\t\t);\n\t}\n\n\tif (!normalized.includes(\"-\")) {\n\t\tthrow new Error(\n\t\t\t`Invalid ${context} \"${normalized}\": custom element names must include a hyphen`,\n\t\t);\n\t}\n\n\tif (RESERVED_TAG_NAMES.has(normalized)) {\n\t\tthrow new Error(\n\t\t\t`Invalid ${context} \"${normalized}\": this name is reserved by the HTML spec`,\n\t\t);\n\t}\n\n\tif (!TAG_FORMAT.test(normalized)) {\n\t\tthrow new Error(\n\t\t\t`Invalid ${context} \"${normalized}\": allowed characters are lowercase letters, numbers, \".\", \"_\" and \"-\"`,\n\t\t);\n\t}\n\n\treturn normalized;\n};\n\n/**\n * Create a deterministic tag for a view-specific element class.\n */\nexport const toViewTag = (\n\tbaseTag: string,\n\tview: PieViewMode | string,\n\tcustomSuffix?: string,\n): string => {\n\tconst suffix =\n\t\tcustomSuffix ??\n\t\t(VIEW_TAG_SUFFIX[view as PieViewMode] !== undefined\n\t\t\t? VIEW_TAG_SUFFIX[view as PieViewMode]\n\t\t\t: \"\");\n\treturn `${baseTag}${suffix}`;\n};\n\n/**\n * Build a safe print tag by appending a deterministic, positive hash suffix.\n */\nexport const toPrintHashedTag = (\n\tbaseTag: string,\n\thashSource: string,\n\thashFn: (value: string) => number,\n): string => {\n\tconst hash = Math.abs(hashFn(hashSource));\n\treturn `${baseTag}-print-${hash}`;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/pie/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAuDH;;GAEG;AACH,MAAM,CAAN,IAAY,UAIX;AAJD,WAAY,UAAU;IACrB,kCAAoB,CAAA;IACpB,+CAAiC,CAAA;IACjC,kCAAoB,CAAA;AACrB,CAAC,EAJW,UAAU,KAAV,UAAU,QAIrB;AAED;;GAEG;AACH,MAAM,CAAN,IAAY,MAGX;AAHD,WAAY,MAAM;IACjB,6BAAmB,CAAA;IACnB,2BAAiB,CAAA;AAClB,CAAC,EAHW,MAAM,KAAN,MAAM,QAGjB;AAsDD;;GAEG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAqC;IACzE,wBAAwB,EAAE,IAAI;IAC9B,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,cAAc,EAAE,KAAK,EAAE;IAC/D,cAAc,EAAE,EAAE;CAClB,CAAC;AAqBF;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,GAAQ;IACtC,OAAO,GAAG,IAAI,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,0BAA0B,CACzC,GAAQ;IAER,OAAO,CACN,OAAO,GAAG,KAAK,UAAU;QACzB,WAAW,IAAI,GAAG;QAClB,GAAG,CAAC,SAAS,YAAY,WAAW,CACpC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACrC,GAAQ;IAER,OAAO,GAAG,IAAI,OAAO,GAAG,CAAC,YAAY,KAAK,QAAQ,CAAC;AACpD,CAAC","sourcesContent":["/**\n * PIE Types Module\n *\n * Pure type definitions, interfaces, and enums used across PIE utilities.\n * This module has NO runtime dependencies on other PIE modules.\n */\n\nimport type {\n\tConfigEntity,\n\tEnv,\n\tPieController,\n\tPieModel,\n} from \"../types/index.js\";\n\n/**\n * Player modes\n */\nexport type PlayerMode = \"gather\" | \"view\" | \"evaluate\" | \"author\";\n\n/**\n * PIE custom element interface\n */\nexport interface PieElement extends HTMLElement {\n\tset model(m: PieModel | undefined);\n\tget model(): PieModel | undefined;\n\tset session(s: any);\n\tget session(): any;\n\tmodelLoaded?: CustomEvent;\n\tonModelChanged: Function;\n\tconfiguration: object;\n}\n\n/**\n * Configure element interface (for authoring mode)\n */\nexport interface ConfigureElement extends HTMLElement {\n\tset model(m: any);\n\tget model(): any;\n\tset configuration(c: any);\n\tget configuration(): any;\n}\n\n/**\n * Authoring environment extends base Env\n */\nexport interface AuthoringEnv extends Env {\n\tmode: \"author\";\n\tconfiguration?: Record<string, any>;\n}\n\n/**\n * Asset upload handler interface (for images and sounds)\n */\nexport interface AssetHandler {\n\tcancel: () => void;\n\tdone: (err?: Error, src?: string) => void;\n\tfileChosen: (file: File) => void;\n\tprogress?: (percent: number, bytes: number, total: number) => void;\n}\n\n/**\n * PIE bundle types\n */\nexport enum BundleType {\n\tplayer = \"player.js\", // Elements only (no controllers)\n\tclientPlayer = \"client-player.js\", // Elements + controllers\n\teditor = \"editor.js\", // Editor UI\n}\n\n/**\n * Internal PIE registry entry status\n */\nexport enum Status {\n\tloading = \"loading\",\n\tloaded = \"loaded\",\n}\n\n/**\n * Internal PIE registry entry\n */\nexport interface Entry {\n\tpackage: string;\n\tstatus: Status;\n\ttagName: string;\n\tcontroller?: PieController;\n\t// Note: In practice these are not DOM Elements; they may be constructors, metadata, etc.\n\t// Keep them loose to support both IIFE and ESM loaders.\n\tconfig?: any;\n\telement?: any;\n\tbundleType?: BundleType; // Track which bundle type was used\n}\n\n/**\n * PIE registry - maps element names to their loaded data\n */\nexport interface PieRegistry {\n\t[key: string]: Entry;\n}\n\n/**\n * Event listeners map\n */\nexport interface EventListeners {\n\t[key: string]: (event: CustomEvent) => void;\n}\n\n/**\n * Options for updating PIE elements\n */\nexport interface UpdatePieElementOptions {\n\tconfig: ConfigEntity;\n\tsession: any[];\n\tenv?: Env;\n\tinvokeControllerForModel?: boolean;\n\teventListeners?: EventListeners;\n\tcontainer?: Element | Document; // Optional container to scope querySelector (defaults to document)\n\t/**\n\t * Invoked when an element controller persists derived session state via its\n\t * `updateSession` callback (e.g. a shuffled-choice order). Lets the host write\n\t * the update back to the authoritative session so subsequent renders reuse it\n\t * instead of regenerating it. Element-agnostic: keyed by element id.\n\t */\n\tonElementSessionUpdate?: (\n\t\telementId: string,\n\t\telementName: string,\n\t\tproperties: Record<string, unknown>,\n\t) => void;\n}\n\n/**\n * Default options for updatePieElement\n */\nexport const defaultPieElementOptions: Partial<UpdatePieElementOptions> = {\n\tinvokeControllerForModel: true,\n\tenv: { mode: \"gather\", role: \"student\", partialScoring: false },\n\teventListeners: {},\n};\n\n/**\n * Event listeners map for multiple elements\n */\nexport interface EventListenersMap {\n\t[elementName: string]: EventListeners;\n}\n\n/**\n * Options for loading PIE elements\n */\nexport interface LoadPieElementsOptions {\n\tbuildServiceBase?: string;\n\tbundleType?: BundleType;\n\tenv?: Env;\n\tbundleUrl?: string;\n\teventListeners?: EventListenersMap;\n\tcontainer?: Element | Document; // Optional container to scope querySelector\n}\n\n/**\n * Type guard: Check if object has window.pie\n */\nexport function isPieAvailable(obj: any): obj is { pie: any } {\n\treturn obj && typeof obj.pie === \"object\";\n}\n\n/**\n * Type guard: Check if object is a CustomElementConstructor\n */\nexport function isCustomElementConstructor(\n\tobj: any,\n): obj is CustomElementConstructor {\n\treturn (\n\t\ttypeof obj === \"function\" &&\n\t\t\"prototype\" in obj &&\n\t\tobj.prototype instanceof HTMLElement\n\t);\n}\n\n/**\n * Type guard: Check if window has PIE_REGISTRY\n */\nexport function isPieRegistryAvailable(\n\tobj: any,\n): obj is { PIE_REGISTRY: PieRegistry } {\n\treturn obj && typeof obj.PIE_REGISTRY === \"object\";\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"updates.js","sourceRoot":"","sources":["../../src/pie/updates.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,iCAAiC,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAE9E,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,+FAA+F;AAC/F,MAAM,MAAM,GAAG,eAAe,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,oBAAoB,EAAE,CAAC,CAAC;AAW5E,MAAM,uBAAuB,GAAG,CAAC,UAAmB,EAAU,EAAE;IAC/D,IAAI,CAAC,UAAU;QAAE,OAAO,SAAS,CAAC;IAClC,IAAI,OAAO,UAAU,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IACxD,IAAI,OAAO,UAAU,KAAK,QAAQ;QAAE,OAAO,OAAO,UAAU,CAAC;IAC7D,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,UAAqC,CAAC,CAAC;IAChE,MAAM,YAAY,GAAI,UAAsC,CAAC,OAAO,CAAC;IACrE,MAAM,WAAW,GAChB,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ;QAC/C,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,YAAuC,CAAC;QACtD,CAAC,CAAC,EAAE,CAAC;IACP,OAAO,WAAW,CAAC,MAAM,GAAG,CAAC;QAC5B,CAAC,CAAC,gBAAgB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI;QAC3E,CAAC,CAAC,gBAAgB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;AACvC,CAAC,CAAC;AAEF,MAAM,8BAA8B,GAAG,CACtC,UAAmB,EAGZ,EAAE;IACT,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,IAAI,OAAO,UAAU,KAAK,UAAU,EAAE,CAAC;QACtC,OAAO,UAKK,CAAC;IACd,CAAC;IACD,IAAI,OAAO,UAAU,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAChD,MAAM,MAAM,GAAI,UAAsC,CAAC,KAAK,CAAC;IAC7D,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QAClC,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,aAAa,EAAE,EAAE,CAChD,MAAmB,CAAC,IAAI,CACxB,UAAU,EACV,KAAK,EACL,WAAW,EACX,GAAG,EACH,aAAa,CACb,CAAC;IACJ,CAAC;IACD,MAAM,WAAW,GAAI,UAAsC,CAAC,OAAO,CAAC;IACpE,IAAI,WAAW,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACpD,MAAM,MAAM,GAAI,WAAuC,CAAC,KAAK,CAAC;QAC9D,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;YAClC,OAAO,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,EAAE,aAAa,EAAE,EAAE,CAChD,MAAmB,CAAC,IAAI,CACxB,WAAW,EACX,KAAK,EACL,WAAW,EACX,GAAG,EACH,aAAa,CACb,CAAC;QACJ,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,CAC3B,UAAsB,EACtB,MAA6B,EACtB,EAAE;IACT,UAAU,CAAC,aAAa,CACvB,IAAI,WAAW,CAAC,sBAAsB,EAAE;QACvC,MAAM;QACN,OAAO,EAAE,IAAI;QACb,QAAQ,EAAE,IAAI;KACd,CAAC,CACF,CAAC;AACH,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,wBAAwB,GAAG,KAAK,EACrC,OAAmB,EACnB,KAAe,EACf,cAAmB,EACnB,UAAmB,EACnB,GAAQ,EACR,SAAiB,EACjB,sBAIS,EACO,EAAE;IAClB,MAAM,CAAC,KAAK,CAAC,GAAG,SAAS,yBAAyB,EAAE,GAAG,CAAC,CAAC;IACzD,MAAM,CAAC,KAAK,CAAC,GAAG,SAAS,uBAAuB,EAAE;QACjD,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,kBAAkB,EAAE,iBAAiB,IAAI,KAAK;KAC9C,CAAC,CAAC;IAEH,2EAA2E;IAC3E,+EAA+E;IAC/E,6EAA6E;IAC7E,+EAA+E;IAC/E,iFAAiF;IACjF,sEAAsE;IACtE,MAAM,aAAa,GAAG,CAAC,EAAU,EAAE,YAAoB,EAAE,UAAe,EAAE,EAAE;QAC3E,MAAM,CAAC,KAAK,CACX,GAAG,SAAS,6BAA6B,EAAE,QAAQ,EACnD,UAAU,CACV,CAAC;QACF,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAC1C,sBAAsB,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAC9D,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC,CAAC;IAEF,IAAI,CAAC;QACJ,MAAM,aAAa,GAAG,8BAA8B,CAAC,UAAU,CAAC,CAAC;QACjE,IAAI,CAAC,aAAa,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACd,0EAA0E,uBAAuB,CAChG,UAAU,CACV,EAAE,CACH,CAAC;QACH,CAAC;QACD,MAAM,gBAAgB,GAAG,MAAM,aAAa,CAC3C,KAAK,EACL,cAAc,EACd,GAAG,EACH,aAAa,CACb,CAAC;QAEF,4FAA4F;QAC5F,MAAM,sBAAsB,GAC3B,gBAAgB,IAAI,OAAO,gBAAgB,KAAK,QAAQ;YACvD,CAAC,CAAE,gBAA4C;YAC/C,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,aAAa,GAAG;YACrB,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,GAAG,sBAAsB;SACzB,CAAC;QACF,MAAM,YAAY,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAC;QAEzD,MAAM,CAAC,KAAK,CAAC,GAAG,SAAS,+BAA+B,EAAE;YACzD,EAAE,EAAE,YAAY,CAAC,EAAE;YACnB,OAAO,EAAE,YAAY,CAAC,OAAO;YAC7B,kBAAkB,EAAE,iBAAiB,IAAI,YAAY;YACrD,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,IAAI,EAAE,GAAG,CAAC,IAAI;SACd,CAAC,CAAC;QAEH,OAAO,CAAC,KAAK,GAAG,YAAY,CAAC;QAC7B,OAAO,CAAC,OAAO,GAAG,cAAc,CAAC;IAClC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,MAAM,CAAC,KAAK,CAAC,GAAG,SAAS,sBAAsB,EAAE,GAAG,CAAC,CAAC;QACtD,MAAM,GAAG,CAAC,CAAC,yCAAyC;IACrD,CAAC;AACF,CAAC,CAAC;AAYF,MAAM,+BAA+B,GAAG,CACvC,IAA6B,EACL,EAAE;IAC1B,MAAM,EACL,MAAM,EACN,OAAO,EACP,GAAG,EACH,cAAc,EACd,wBAAwB,EACxB,sBAAsB,GACtB,GAAG,iCAAiC,CAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC;IACtE,IAAI,CAAC,GAAG,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,oBAAoB,CAAC,CAAC;IACvC,CAAC;IACD,OAAO;QACN,MAAM;QACN,OAAO;QACP,GAAG;QACH,cAAc;QACd,wBAAwB;QACxB,sBAAsB;KACtB,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,sBAAsB,GAAG,KAAK,EACnC,UAAsB,EACtB,mBAA2B,EAC3B,OAA8B,EAC9B,UAAkB,EACF,EAAE;IAClB,MAAM,EACL,MAAM,EACN,OAAO,EACP,GAAG,EACH,cAAc,EACd,wBAAwB,EACxB,sBAAsB,GACtB,GAAG,OAAO,CAAC;IACZ,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,UAAU,CAAC,EAAE,CAEnD,CAAC;IACb,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,CAAC,KAAK,CAAC,GAAG,UAAU,sBAAsB,EAAE,mBAAmB,CAAC,CAAC;QACvE,MAAM,IAAI,KAAK,CAAC,uBAAuB,mBAAmB,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,cAAc,GAAG,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IAE1E,yEAAyE;IACzE,IAAI,cAAc,EAAE,CAAC;QACpB,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;YACpD,UAAU,CAAC,gBAAgB,CAAC,GAAU,EAAE,EAAE,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,IAAI,wBAAwB,EAAE,CAAC;QACrC,MAAM,UAAU,GAAG,iBAAiB,CAAC,mBAAmB,CAAC,CAAC;QAC1D,IAAI,CAAC,UAAU,EAAE,CAAC;YACjB,MAAM,CAAC,KAAK,CACX,GAAG,UAAU,yBAAyB,mBAAmB,gCAAgC,CACzF,CAAC;YACF,UAAU,CAAC,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;YAC/C,UAAU,CAAC,OAAO,GAAG,cAAc,CAAC;YACpC,OAAO;QACR,CAAC;QAED,MAAM,CAAC,KAAK,CACX,GAAG,UAAU,4BAA4B,mBAAmB,IAAI,UAAU,CAAC,EAAE,EAAE,EAC/E;YACC,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,kBAAkB,EAAE,iBAAiB,IAAI,KAAK;SAC9C,CACD,CAAC;QAEF,4EAA4E;QAC5E,uEAAuE;QACvE,0EAA0E;QAC1E,+BAA+B;QAC/B,IAAI,CAAC;YACJ,MAAM,wBAAwB,CAC7B,UAAU,EACV,KAAK,EACL,cAAc,EACd,UAAU,EACV,GAAG,EACH,GAAG,UAAU,IAAI,mBAAmB,IAAI,UAAU,CAAC,EAAE,GAAG,EACxD,sBAAsB,CACtB,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,MAAM,YAAY,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACtE,MAAM,eAAe,GAAG,YAAY,CAAC,QAAQ,CAC5C,8BAA8B,CAC9B,CAAC;YACF,MAAM,CAAC,KAAK,CACX,GAAG,UAAU,0BAA0B,mBAAmB,IAAI,UAAU,CAAC,EAAE,GAAG,EAC9E,GAAG,CACH,CAAC;YACF,mBAAmB,CAAC,UAAU,EAAE;gBAC/B,IAAI,EAAE,eAAe;oBACpB,CAAC,CAAC,+BAA+B;oBACjC,CAAC,CAAC,8BAA8B;gBACjC,OAAO,EAAE,GAAG,mBAAmB,+CAA+C,UAAU,CAAC,EAAE,KAAK,YAAY,EAAE;gBAC9G,WAAW,EAAE,mBAAmB;gBAChC,SAAS,EAAE,UAAU,CAAC,EAAE;gBACxB,eAAe,EAAE,uBAAuB,CAAC,UAAU,CAAC;gBACpD,KAAK,EAAE,YAAY;aACnB,CAAC,CAAC;YACH,6CAA6C;YAC7C,UAAU,CAAC,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;YAC/C,UAAU,CAAC,OAAO,GAAG,cAAc,CAAC;QACrC,CAAC;IACF,CAAC;SAAM,CAAC;QACP,MAAM,CAAC,KAAK,CACX,GAAG,UAAU,gCAAgC,mBAAmB,IAAI,UAAU,CAAC,EAAE,uCAAuC,CACxH,CAAC;QACF,UAAU,CAAC,KAAK,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC/C,UAAU,CAAC,OAAO,GAAG,cAAc,CAAC;IACrC,CAAC;AACF,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACtC,EAAW,EACX,IAA6B,EACb,EAAE;IAClB,MAAM,OAAO,GAAG,+BAA+B,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,UAAU,GAAG,EAAgB,CAAC;IACpC,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IAChD,OAAO,sBAAsB,CAC5B,UAAU,EACV,MAAM,EACN,OAAO,EACP,2BAA2B,CAC3B,CAAC;AACH,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC/B,MAAc,EACd,IAA6B,EACb,EAAE;IAClB,MAAM,OAAO,GAAG,+BAA+B,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,EAAE,SAAS,EAAE,GAAG,iCAAiC,CACtD,wBAAwB,EACxB,IAAI,CACJ,CAAC;IACF,0EAA0E;IAC1E,MAAM,UAAU,GAAG,SAAS,IAAI,QAAQ,CAAC;IACzC,MAAM,WAAW,GAAG,UAAU,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IACxD,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9C,MAAM,CAAC,KAAK,CAAC,yBAAyB,MAAM,EAAE,CAAC,CAAC;QAChD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC1B,CAAC;IACD,OAAO,OAAO,CAAC,GAAG,CACjB,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,CAC9B,sBAAsB,CACrB,EAAgB,EAChB,MAAM,EACN,OAAO,EACP,oBAAoB,CACpB,CACD,CACD,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;AACzB,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAChC,MAAoB,EACpB,OAAc,EACd,GAAQ,EACR,SAA8B,EAC9B,sBAA0E,EAC1D,EAAE;IAClB,MAAM,CAAC,KAAK,CAAC,qDAAqD,EAAE,GAAG,CAAC,CAAC;IACzE,OAAO,OAAO,CAAC,GAAG,CACjB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CACtD,gBAAgB,CAAC,MAAM,EAAE;QACxB,MAAM;QACN,OAAO;QACP,GAAG;QACH,SAAS;QACT,sBAAsB;KACtB,CAAC,CACF,CACD,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;AACzB,CAAC,CAAC","sourcesContent":["/**\n * PIE Updates Module\n *\n * Functions for updating PIE elements with new models, sessions, and env.\n */\n\nimport { mergeObjectsIgnoringNullUndefined } from \"../object/index.js\";\nimport { wrapModelRichContent } from \"../security/wrap-model-rich-content.js\";\nimport type { ConfigEntity, Env, PieModel } from \"../types/index.js\";\nimport { createPieLogger, isGlobalDebugEnabled } from \"./logger.js\";\nimport { findPieController } from \"./scoring.js\";\nimport type { PieElement, UpdatePieElementOptions } from \"./types.js\";\nimport { defaultPieElementOptions } from \"./types.js\";\nimport { findOrAddSession } from \"./utils.js\";\n\n// Create module-level logger (respects global debug flag - pass function for dynamic checking)\nconst logger = createPieLogger(\"pie-updates\", () => isGlobalDebugEnabled());\n\ntype ControllerErrorDetail = {\n\tcode: \"PIE_CONTROLLER_CONTRACT_ERROR\" | \"PIE_CONTROLLER_RUNTIME_ERROR\";\n\tmessage: string;\n\telementName: string;\n\telementId: string;\n\tcontrollerShape?: string;\n\tcause?: string;\n};\n\nconst describeControllerShape = (controller: unknown): string => {\n\tif (!controller) return \"missing\";\n\tif (typeof controller === \"function\") return \"function\";\n\tif (typeof controller !== \"object\") return typeof controller;\n\tconst keys = Object.keys(controller as Record<string, unknown>);\n\tconst defaultValue = (controller as Record<string, unknown>).default;\n\tconst defaultKeys =\n\t\tdefaultValue && typeof defaultValue === \"object\"\n\t\t\t? Object.keys(defaultValue as Record<string, unknown>)\n\t\t\t: [];\n\treturn defaultKeys.length > 0\n\t\t? `object(keys=[${keys.join(\",\")}],defaultKeys=[${defaultKeys.join(\",\")}])`\n\t\t: `object(keys=[${keys.join(\",\")}])`;\n};\n\nconst resolveControllerModelFunction = (\n\tcontroller: unknown,\n):\n\t| ((model: PieModel, session: any, env: Env, updateSession: any) => unknown)\n\t| null => {\n\tif (!controller) return null;\n\tif (typeof controller === \"function\") {\n\t\treturn controller as (\n\t\t\tmodel: PieModel,\n\t\t\tsession: any,\n\t\t\tenv: Env,\n\t\t\tupdateSession: any,\n\t\t) => unknown;\n\t}\n\tif (typeof controller !== \"object\") return null;\n\tconst direct = (controller as Record<string, unknown>).model;\n\tif (typeof direct === \"function\") {\n\t\treturn (model, sessionData, env, updateSession) =>\n\t\t\t(direct as Function).call(\n\t\t\t\tcontroller,\n\t\t\t\tmodel,\n\t\t\t\tsessionData,\n\t\t\t\tenv,\n\t\t\t\tupdateSession,\n\t\t\t);\n\t}\n\tconst fromDefault = (controller as Record<string, unknown>).default;\n\tif (fromDefault && typeof fromDefault === \"object\") {\n\t\tconst nested = (fromDefault as Record<string, unknown>).model;\n\t\tif (typeof nested === \"function\") {\n\t\t\treturn (model, sessionData, env, updateSession) =>\n\t\t\t\t(nested as Function).call(\n\t\t\t\t\tfromDefault,\n\t\t\t\t\tmodel,\n\t\t\t\t\tsessionData,\n\t\t\t\t\tenv,\n\t\t\t\t\tupdateSession,\n\t\t\t\t);\n\t\t}\n\t}\n\treturn null;\n};\n\nconst emitControllerError = (\n\tpieElement: PieElement,\n\tdetail: ControllerErrorDetail,\n): void => {\n\tpieElement.dispatchEvent(\n\t\tnew CustomEvent(\"pie-controller-error\", {\n\t\t\tdetail,\n\t\t\tbubbles: true,\n\t\t\tcomposed: true,\n\t\t}),\n\t);\n};\n\n/**\n * Helper function to apply controller to element\n * Extracted to eliminate duplication and ensure consistent controller invocation\n */\nconst applyControllerToElement = async (\n\telement: PieElement,\n\tmodel: PieModel,\n\telementSession: any,\n\tcontroller: unknown,\n\tenv: Env,\n\tlogPrefix: string,\n\tonElementSessionUpdate?: (\n\t\telementId: string,\n\t\telementName: string,\n\t\tproperties: Record<string, unknown>,\n\t) => void,\n): Promise<void> => {\n\tlogger.debug(`${logPrefix} Using controller, env:`, env);\n\tlogger.debug(`${logPrefix} Model before filter:`, {\n\t\tid: model.id,\n\t\telement: model.element,\n\t\thasCorrectResponse: \"correctResponse\" in model,\n\t});\n\n\t// Create updateSession callback for controller to save derived state (e.g.\n\t// shuffle order). Mutate the in-flight element session so this render uses it,\n\t// AND propagate the write to the authoritative session via the host callback\n\t// so subsequent renders reuse it instead of regenerating it. The write-back is\n\t// keyed by the canonical model id/element (the entry findOrAddSession resolved),\n\t// which also tolerates controllers that pass an undefined id/element.\n\tconst updateSession = (id: string, _elementName: string, properties: any) => {\n\t\tlogger.debug(\n\t\t\t`${logPrefix} updateSession called for ${id} with:`,\n\t\t\tproperties,\n\t\t);\n\t\tObject.assign(elementSession, properties);\n\t\tonElementSessionUpdate?.(model.id, model.element, properties);\n\t\treturn Promise.resolve();\n\t};\n\n\ttry {\n\t\tconst modelFunction = resolveControllerModelFunction(controller);\n\t\tif (!modelFunction) {\n\t\t\tthrow new Error(\n\t\t\t\t`Controller contract mismatch: expected a model() function but received ${describeControllerShape(\n\t\t\t\t\tcontroller,\n\t\t\t\t)}`,\n\t\t\t);\n\t\t}\n\t\tconst controllerResult = await modelFunction(\n\t\t\tmodel,\n\t\t\telementSession,\n\t\t\tenv,\n\t\t\tupdateSession,\n\t\t);\n\n\t\t// Merge controller result with id and element (like server-side PieControllerExecutor does)\n\t\tconst controllerResultObject =\n\t\t\tcontrollerResult && typeof controllerResult === \"object\"\n\t\t\t\t? (controllerResult as Record<string, unknown>)\n\t\t\t\t: {};\n\t\tconst filteredModel = {\n\t\t\tid: model.id,\n\t\t\telement: model.element,\n\t\t\t...controllerResultObject,\n\t\t};\n\t\tconst wrappedModel = wrapModelRichContent(filteredModel);\n\n\t\tlogger.debug(`${logPrefix} ✅ Controller filtered model:`, {\n\t\t\tid: wrappedModel.id,\n\t\t\telement: wrappedModel.element,\n\t\t\thasCorrectResponse: \"correctResponse\" in wrappedModel,\n\t\t\tmode: env.mode,\n\t\t\trole: env.role,\n\t\t});\n\n\t\telement.model = wrappedModel;\n\t\telement.session = elementSession;\n\t} catch (err) {\n\t\tlogger.error(`${logPrefix} ❌ Controller error:`, err);\n\t\tthrow err; // Re-throw - controller errors are fatal\n\t}\n};\n\ntype ResolvedUpdateOptions = Pick<\n\tUpdatePieElementOptions,\n\t| \"config\"\n\t| \"session\"\n\t| \"env\"\n\t| \"eventListeners\"\n\t| \"invokeControllerForModel\"\n\t| \"onElementSessionUpdate\"\n>;\n\nconst resolveAndValidateUpdateOptions = (\n\topts: UpdatePieElementOptions,\n): ResolvedUpdateOptions => {\n\tconst {\n\t\tconfig,\n\t\tsession,\n\t\tenv,\n\t\teventListeners,\n\t\tinvokeControllerForModel,\n\t\tonElementSessionUpdate,\n\t} = mergeObjectsIgnoringNullUndefined(defaultPieElementOptions, opts);\n\tif (!env) {\n\t\tthrow new Error(\"env is required\");\n\t}\n\tif (!session) {\n\t\tthrow new Error(\"session is required\");\n\t}\n\tif (!config) {\n\t\tthrow new Error(\"config is required\");\n\t}\n\treturn {\n\t\tconfig,\n\t\tsession,\n\t\tenv,\n\t\teventListeners,\n\t\tinvokeControllerForModel,\n\t\tonElementSessionUpdate,\n\t};\n};\n\nconst updateSinglePieElement = async (\n\tpieElement: PieElement,\n\tcontrollerLookupTag: string,\n\toptions: ResolvedUpdateOptions,\n\tlogContext: string,\n): Promise<void> => {\n\tconst {\n\t\tconfig,\n\t\tsession,\n\t\tenv,\n\t\teventListeners,\n\t\tinvokeControllerForModel,\n\t\tonElementSessionUpdate,\n\t} = options;\n\tconst model = config.models?.find((m) => m.id === pieElement.id) as\n\t\t| PieModel\n\t\t| undefined;\n\tif (!model) {\n\t\tlogger.error(`${logContext} Model not found for`, controllerLookupTag);\n\t\tthrow new Error(`model not found for ${controllerLookupTag}`);\n\t}\n\n\tconst elementSession = findOrAddSession(session, model.id, model.element);\n\n\t// Always attach event listeners (don't skip them for no-controller case)\n\tif (eventListeners) {\n\t\tObject.entries(eventListeners).forEach(([evt, fn]) => {\n\t\t\tpieElement.addEventListener(evt as any, fn);\n\t\t});\n\t}\n\n\tif (env && invokeControllerForModel) {\n\t\tconst controller = findPieController(controllerLookupTag);\n\t\tif (!controller) {\n\t\t\tlogger.debug(\n\t\t\t\t`${logContext} ℹ️ No controller for ${controllerLookupTag}, using server-processed model`,\n\t\t\t);\n\t\t\tpieElement.model = wrapModelRichContent(model);\n\t\t\tpieElement.session = elementSession;\n\t\t\treturn;\n\t\t}\n\n\t\tlogger.debug(\n\t\t\t`${logContext} Invoking controller for ${controllerLookupTag}#${pieElement.id}`,\n\t\t\t{\n\t\t\t\tmode: env.mode,\n\t\t\t\trole: env.role,\n\t\t\t\thasCorrectResponse: \"correctResponse\" in model,\n\t\t\t},\n\t\t);\n\n\t\t// Await the controller so any updateSession write-back completes before the\n\t\t// caller computes/emits the session signature; otherwise a late, async\n\t\t// shuffle write lands after the cycle that read the session and the order\n\t\t// never round-trips (PIE-631).\n\t\ttry {\n\t\t\tawait applyControllerToElement(\n\t\t\t\tpieElement,\n\t\t\t\tmodel,\n\t\t\t\telementSession,\n\t\t\t\tcontroller,\n\t\t\t\tenv,\n\t\t\t\t`${logContext}(${controllerLookupTag}#${pieElement.id})`,\n\t\t\t\tonElementSessionUpdate,\n\t\t\t);\n\t\t} catch (err) {\n\t\t\tconst errorMessage = err instanceof Error ? err.message : String(err);\n\t\t\tconst isContractError = errorMessage.includes(\n\t\t\t\t\"Controller contract mismatch\",\n\t\t\t);\n\t\t\tlogger.error(\n\t\t\t\t`${logContext} Controller failed for ${controllerLookupTag}#${pieElement.id}:`,\n\t\t\t\terr,\n\t\t\t);\n\t\t\temitControllerError(pieElement, {\n\t\t\t\tcode: isContractError\n\t\t\t\t\t? \"PIE_CONTROLLER_CONTRACT_ERROR\"\n\t\t\t\t\t: \"PIE_CONTROLLER_RUNTIME_ERROR\",\n\t\t\t\tmessage: `${controllerLookupTag} controller failed while applying model for ${pieElement.id}. ${errorMessage}`,\n\t\t\t\telementName: controllerLookupTag,\n\t\t\t\telementId: pieElement.id,\n\t\t\t\tcontrollerShape: describeControllerShape(controller),\n\t\t\t\tcause: errorMessage,\n\t\t\t});\n\t\t\t// Fall back to raw model on controller error\n\t\t\tpieElement.model = wrapModelRichContent(model);\n\t\t\tpieElement.session = elementSession;\n\t\t}\n\t} else {\n\t\tlogger.debug(\n\t\t\t`${logContext} Direct model assignment for ${controllerLookupTag}#${pieElement.id} (no controller invocation requested)`,\n\t\t);\n\t\tpieElement.model = wrapModelRichContent(model);\n\t\tpieElement.session = elementSession;\n\t}\n};\n\n/**\n * Update a PIE element by ref (direct Element reference)\n */\nexport const updatePieElementWithRef = (\n\tel: Element,\n\topts: UpdatePieElementOptions,\n): Promise<void> => {\n\tconst options = resolveAndValidateUpdateOptions(opts);\n\tconst pieElement = el as PieElement;\n\tconst elName = pieElement.tagName.toLowerCase();\n\treturn updateSinglePieElement(\n\t\tpieElement,\n\t\telName,\n\t\toptions,\n\t\t\"[updatePieElementWithRef]\",\n\t);\n};\n\n/**\n * Update a PIE element by name (finds all matching elements in DOM)\n */\nexport const updatePieElement = (\n\telName: string,\n\topts: UpdatePieElementOptions,\n): Promise<void> => {\n\tconst options = resolveAndValidateUpdateOptions(opts);\n\tconst { container } = mergeObjectsIgnoringNullUndefined(\n\t\tdefaultPieElementOptions,\n\t\topts,\n\t);\n\t// Use container for scoped query or fallback to document for global query\n\tconst searchRoot = container || document;\n\tconst pieElements = searchRoot.querySelectorAll(elName);\n\tif (!pieElements || pieElements.length === 0) {\n\t\tlogger.debug(`no elements found for ${elName}`);\n\t\treturn Promise.resolve();\n\t}\n\treturn Promise.all(\n\t\tArray.from(pieElements, (el) =>\n\t\t\tupdateSinglePieElement(\n\t\t\t\tel as PieElement,\n\t\t\t\telName,\n\t\t\t\toptions,\n\t\t\t\t\"[updatePieElement]\",\n\t\t\t),\n\t\t),\n\t).then(() => undefined);\n};\n\n/**\n * Update all PIE elements in a config\n */\nexport const updatePieElements = (\n\tconfig: ConfigEntity,\n\tsession: any[],\n\tenv: Env,\n\tcontainer?: Element | Document,\n\tonElementSessionUpdate?: UpdatePieElementOptions[\"onElementSessionUpdate\"],\n): Promise<void> => {\n\tlogger.debug(\"[updatePieElements] Updating all elements with env:\", env);\n\treturn Promise.all(\n\t\tObject.entries(config.elements).map(([elName, _pkg]) =>\n\t\t\tupdatePieElement(elName, {\n\t\t\t\tconfig,\n\t\t\t\tsession,\n\t\t\t\tenv,\n\t\t\t\tcontainer,\n\t\t\t\tonElementSessionUpdate,\n\t\t\t}),\n\t\t),\n\t).then(() => undefined);\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/pie/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH;;GAEG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACtC,MAAoB,EACpB,IAA4B,EACnB,EAAE;IACX,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IACjC,OAAO,GAAG,IAAI,CAAC,gBAAgB,IAAI,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;AACtG,CAAC,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC/B,KAAa,EACqC,EAAE;IACpD,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;IACjD,CAAC;IACD,MAAM,OAAO,GACZ,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG;QACtB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC,4BAA4B;QAC5F,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC,CAAC,sBAAsB;IACjF,IAAI,CAAC,OAAO,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,yBAAyB,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO;QACN,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QAChB,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE;QACtB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE;KACzB,CAAC;AACH,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,QAAgB,EAAU,EAAE,CACpE,QAAQ;KACN,KAAK,CAAC,GAAG,CAAC;KACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;KACpC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEb;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC/B,IAAW,EACX,EAAU,EACV,OAAgB,EACV,EAAE;IACR,IAAI,CAAC,IAAI,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAC;IAC7C,CAAC;IACD,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACxC,IAAI,CAAC,EAAE,CAAC;QACP,0DAA0D;QAC1D,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;YAC3B,CAAC,CAAC,OAAO,GAAG,OAAO,CAAC;QACrB,CAAC;QACD,OAAO,CAAC,CAAC;IACV,CAAC;IACD,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC;IAC9C,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACd,OAAO,EAAE,CAAC;AACX,CAAC,CAAC","sourcesContent":["/**\n * PIE Utils Module\n *\n * URL building, package name parsing, and session utilities.\n */\n\nimport type { ConfigEntity } from \"../types/index.js\";\nimport type { LoadPieElementsOptions } from \"./types.js\";\n\n/**\n * Build URL for fetching PIE element bundles from build service\n */\nexport const getPieElementBundlesUrl = (\n\tconfig: ConfigEntity,\n\topts: LoadPieElementsOptions,\n): string => {\n\tconst elements = config.elements;\n\treturn `${opts.buildServiceBase}/${encodeURI(Object.values(elements).join(\"+\"))}/${opts.bundleType}`;\n};\n\n/**\n * Parse a package name string into its components\n *\n * NOTE: Duplicated because we can't have any server-side code in the client\n *\n * Examples:\n * - \"@pie-element/multiple-choice@9.9.1\" → { name: \"@pie-element/multiple-choice\", path: \"\", version: \"9.9.1\" }\n * - \"lodash/get@4.4.2\" → { name: \"lodash\", path: \"get\", version: \"4.4.2\" }\n */\nexport const parsePackageName = (\n\tinput: string,\n): { name: string; path: string; version: string } => {\n\tif (!input) {\n\t\tthrow new Error(\"Parameter is required: input\");\n\t}\n\tconst matched =\n\t\tinput.charAt(0) === \"@\"\n\t\t\t? input.match(/^(@[^/]+\\/[^/@]+)(?:\\/([^@]+))?(?:@([\\s\\S]+))?/) // scoped package name regex\n\t\t\t: input.match(/^([^/@]+)(?:\\/([^@]+))?(?:@([\\s\\S]+))?/); // normal package name\n\tif (!matched) {\n\t\tthrow new Error(`[parse-package-name] \"${input}\" is not a valid string`);\n\t}\n\treturn {\n\t\tname: matched[1],\n\t\tpath: matched[2] || \"\",\n\t\tversion: matched[3] || \"\",\n\t};\n};\n\n/**\n * Strip versions from a package string\n *\n * Example: \"@pie-element/multiple-choice@9.9.1+@pie-element/hotspot@9.1.0\"\n * → \"@pie-element/multiple-choice+@pie-element/hotspot\"\n */\nexport const getPackageWithoutVersion = (packages: string): string =>\n\tpackages\n\t\t.split(\"+\")\n\t\t.map((p) => parsePackageName(p).name)\n\t\t.join(\"+\");\n\n/**\n * Find or add a session entry for a given element\n *\n * TODO: kinda gnarly, copied from player project\n *\n * @param data - Session data array\n * @param id - Model/element ID\n * @param element - Element tag name (optional)\n * @returns The session entry\n */\nexport const findOrAddSession = (\n\tdata: any[],\n\tid: string,\n\telement?: string,\n): any => {\n\tif (!data) {\n\t\tthrow new Error(\"session data is required\");\n\t}\n\tconst s = data.find((d) => d.id === id);\n\tif (s) {\n\t\t// Update element property if provided and not already set\n\t\tif (element && !s.element) {\n\t\t\ts.element = element;\n\t\t}\n\t\treturn s;\n\t}\n\tconst ss = element ? { id, element } : { id };\n\tdata.push(ss);\n\treturn ss;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"versioned-tag.js","sourceRoot":"","sources":["../../src/pie/versioned-tag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,iBAAiB,GAAG,YAAY,CAAC;AACvC,MAAM,mBAAmB,GAAG,eAAe,CAAC;AAE5C,MAAM,mBAAmB,GAAG,CAAC,OAAe,EAAU,EAAE,CACvD,OAAO;KACL,IAAI,EAAE;KACN,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;KACrB,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC;KAC9B,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAE1B,MAAM,UAAU,qBAAqB,CAAC,OAAe;IAIpD,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CACjC,IAAI,MAAM,CAAC,GAAG,iBAAiB,IAAI,mBAAmB,IAAI,CAAC,CAC3D,CAAC;IACF,IAAI,CAAC,YAAY,IAAI,YAAY,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACvD,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO;QACN,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC;QAC9C,sBAAsB,EAAE,YAAY,CAAC,CAAC,CAAC;KACvC,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,qBAAqB,CACpC,OAAe,EACf,WAAmB,EACnB,UAAgD,EAAE;IAElD,MAAM,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAElD,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC,sBAAsB,EAAE,CAAC;QACvD,yEAAyE;QACzE,wDAAwD;QACxD,OAAO,OAAO,CAAC;IAChB,CAAC;IAED,MAAM,oBAAoB,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC1D,MAAM,EAAE,QAAQ,EAAE,sBAAsB,EAAE,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC5E,IAAI,sBAAsB,KAAK,oBAAoB,EAAE,CAAC;QACrD,OAAO,OAAO,CAAC;IAChB,CAAC;IAED,8EAA8E;IAC9E,+EAA+E;IAC/E,OAAO,GAAG,QAAQ,GAAG,iBAAiB,GAAG,oBAAoB,EAAE,CAAC;AACjE,CAAC","sourcesContent":["import { parsePackageName } from \"./utils.js\";\n\nconst VERSION_DELIMITER = \"--version-\";\nconst TAG_VERSION_PATTERN = \"[0-9A-Za-z-]+\";\n\nconst encodeVersionForTag = (version: string): string =>\n\tversion\n\t\t.trim()\n\t\t.replace(/[.+]/g, \"-\")\n\t\t.replace(/[^0-9A-Za-z-]/g, \"-\")\n\t\t.replace(/-{2,}/g, \"-\");\n\nexport function parseVersionedTagName(tagName: string): {\n\tbaseName: string;\n\texistingEncodedVersion?: string;\n} {\n\tconst versionMatch = tagName.match(\n\t\tnew RegExp(`${VERSION_DELIMITER}(${TAG_VERSION_PATTERN})$`),\n\t);\n\tif (!versionMatch || versionMatch.index === undefined) {\n\t\treturn { baseName: tagName };\n\t}\n\treturn {\n\t\tbaseName: tagName.slice(0, versionMatch.index),\n\t\texistingEncodedVersion: versionMatch[1],\n\t};\n}\n\n/**\n * Return the full runtime tag for an element package spec.\n *\n * The version suffix acts as an effective namespace in the immutable browser\n * CustomElementRegistry, allowing several package versions to coexist. Existing\n * versioned inputs are normalized on the runtime copy when a host substitutes a\n * different package version. The caller's authored config is not mutated.\n */\nexport function toPackageVersionedTag(\n\ttagName: string,\n\tpackageSpec: string,\n\toptions: { preserveUnversionedTag?: boolean } = {},\n): string {\n\tconst { version } = parsePackageName(packageSpec);\n\n\tif (!version.trim() && options.preserveUnversionedTag) {\n\t\t// aggregateElements historically kept unversioned package specs on their\n\t\t// authored tag. Preserve that caller-specific behavior.\n\t\treturn tagName;\n\t}\n\n\tconst targetEncodedVersion = encodeVersionForTag(version);\n\tconst { baseName, existingEncodedVersion } = parseVersionedTagName(tagName);\n\tif (existingEncodedVersion === targetEncodedVersion) {\n\t\treturn tagName;\n\t}\n\n\t// pie-item contract compatibility: preloaded hosts have historically replaced\n\t// package versions at runtime, which requires updating the cloned runtime tag.\n\treturn `${baseName}${VERSION_DELIMITER}${targetEncodedVersion}`;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"player-strategy.js","sourceRoot":"","sources":["../src/player-strategy.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,2BAA2B,CAC1C,KAAgC,EAChC,WAA+B,MAAM;IAErC,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;QAClE,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,qBAAqB,CACpC,IAA6B,EAC7B,cAA8B,UAAU;IAExC,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO,QAAQ,CAAC;IACjB,CAAC;IACD,OAAO,WAAW,CAAC;AACpB,CAAC","sourcesContent":["import type { Env } from \"./types/index.js\";\n\nexport type ItemPlayerStrategy = \"iife\" | \"esm\" | \"preloaded\";\nexport type ItemPlayerView = \"delivery\" | \"author\" | \"print\";\n\nexport function normalizeItemPlayerStrategy(\n\tvalue: string | null | undefined,\n\tfallback: ItemPlayerStrategy = \"iife\",\n): ItemPlayerStrategy {\n\tif (value === \"iife\" || value === \"esm\" || value === \"preloaded\") {\n\t\treturn value;\n\t}\n\treturn fallback;\n}\n\nexport function resolveItemPlayerView(\n\tmode: Env[\"mode\"] | undefined,\n\tdefaultView: ItemPlayerView = \"delivery\",\n): ItemPlayerView {\n\tif (mode === \"author\") {\n\t\treturn \"author\";\n\t}\n\treturn defaultView;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/security/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,uBAAuB,EACvB,gCAAgC,EAChC,uBAAuB,EACvB,kBAAkB,GAGlB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,wBAAwB,EACxB,wBAAwB,GAKxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACN,2BAA2B,EAC3B,eAAe,GACf,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,kBAAkB,EAClB,2BAA2B,GAC3B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACpE,OAAO,EACN,kBAAkB,EAClB,2BAA2B,GAC3B,MAAM,2BAA2B,CAAC","sourcesContent":["export {\n\tbuildAuthoringAllowList,\n\tcreateDefaultItemMarkupSanitizer,\n\tresetPurifierForTesting,\n\tsanitizeItemMarkup,\n\ttype ItemMarkupSanitizer,\n\ttype SanitizeItemMarkupOptions,\n} from \"./sanitize-item-markup.js\";\nexport {\n\tparseAllowedStyleOrigins,\n\tvalidateExternalStyleUrl,\n\ttype StyleUrlValidationError,\n\ttype StyleUrlValidationOk,\n\ttype StyleUrlValidationOptions,\n\ttype StyleUrlValidationResult,\n} from \"./validate-style-url.js\";\nexport {\n\tresetSvgSanitizerForTesting,\n\tsanitizeSvgIcon,\n} from \"./sanitize-svg-icon.js\";\nexport {\n\twrapOverwideImages,\n\twrapOverwideImagesInElement,\n} from \"./wrap-overwide-images.js\";\nexport { wrapModelRichContent } from \"./wrap-model-rich-content.js\";\nexport {\n\twrapOverwideTables,\n\twrapOverwideTablesInElement,\n} from \"./wrap-overwide-tables.js\";\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"sanitize-item-markup.js","sourceRoot":"","sources":["../../src/security/sanitize-item-markup.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Default sanitizer for PIE item / passage markup.\n *\n * Used by `PieItemPlayer.svelte` to strip scripts, event-handler attributes,\n * and unknown tags before injecting authored markup via `{@html}`. Hosts can\n * opt out with the `trust-markup` attribute on the `<pie-*-player>` element,\n * or supply their own sanitizer function if they need a stricter / looser\n * allow-list.\n */\n\nimport DOMPurify from \"dompurify\";\n\nimport { wrapOverwideImages } from \"./wrap-overwide-images.js\";\nimport { wrapOverwideTables } from \"./wrap-overwide-tables.js\";\n\nexport type ItemMarkupSanitizer = (markup: string) => string;\n\nexport interface SanitizeItemMarkupOptions {\n\t/**\n\t * Extra custom-element tag names that should survive sanitization in\n\t * addition to the default `pie-*` allow-list. Useful for authoring-mode\n\t * tags that rewrite to `pie-*-config` or host-registered extensions.\n\t */\n\tallowedCustomElements?: string[];\n}\n\n// Attributes every PIE element / wrapper is allowed to carry.\nconst BASE_ALLOWED_ATTRS = [\n\t\"slot\",\n\t\"role\",\n\t\"tabindex\",\n\t\"id\",\n\t\"class\",\n\t\"style\",\n\t\"href\",\n\t\"src\",\n\t\"alt\",\n\t\"title\",\n\t\"hidden\",\n\t\"disabled\",\n\t\"lang\",\n\t\"dir\",\n];\n\nconst BASE_URI_SAFE_ATTRS = [\"pie-id\"];\n\nconst FORBIDDEN_TAGS = [\n\t\"script\",\n\t\"iframe\",\n\t\"object\",\n\t\"embed\",\n\t\"base\",\n\t\"form\",\n\t\"meta\",\n\t\"link\",\n\t// <foreignObject> inside an <svg> is a well-known escape hatch back\n\t// into HTML context; match the SVG-icon sanitizer and forbid it here\n\t// so both sanitizers agree on the surface.\n\t\"foreignobject\",\n];\n\n// DOMPurify already strips `on*` handlers via its default block-list;\n// these entries guarantee they stay stripped even if a consumer tweaks\n// defaults, and they cover the common SVG / math sinks.\nconst FORBIDDEN_ATTRS = [\n\t\"onerror\",\n\t\"onload\",\n\t\"onclick\",\n\t\"onmouseover\",\n\t\"onmouseout\",\n\t\"onmouseenter\",\n\t\"onmouseleave\",\n\t\"onfocus\",\n\t\"onblur\",\n\t\"onkeydown\",\n\t\"onkeyup\",\n\t\"onkeypress\",\n\t\"onsubmit\",\n\t\"onchange\",\n\t\"onbeforeunload\",\n\t\"formaction\",\n\t\"xlink:href\",\n];\n\n// Any tag that looks like a custom element (contains a hyphen) is permitted\n// provided it starts with `pie-` or is explicitly named in\n// `allowedCustomElements`. This intentionally keeps third-party unknown\n// custom elements out unless the host opts in.\nconst PIE_CUSTOM_ELEMENT_REGEX = /^pie-[a-z0-9-]+$/i;\n\n// Attribute names that custom elements are allowed to declare. We stay\n// permissive for the PIE element contract (`model-*`, `session-*`, ...) and\n// the standard `data-*` / `aria-*` families.\nconst CUSTOM_ELEMENT_ATTR_REGEX =\n\t/^(id|class|style|slot|role|tabindex|hidden|disabled|lang|dir|data-[\\w-]+|aria-[\\w-]+|pie-[\\w-]+|model-[\\w-]+|session-[\\w-]+|config-[\\w-]+|context-[\\w-]+)$/i;\n\ninterface DOMPurifyInstance {\n\tsanitize: (\n\t\tsource: string,\n\t\tconfig?: Record<string, unknown>,\n\t) => string | Node | DocumentFragment;\n}\n\nlet purifierInstance: DOMPurifyInstance | null = null;\n\nfunction resolvePurifier(): DOMPurifyInstance | null {\n\tif (purifierInstance) return purifierInstance;\n\tif (typeof window === \"undefined\" || !window.document) return null;\n\t// DOMPurify's default export is both the instance and the factory.\n\t// Calling it with a window binds the instance to that document.\n\tconst factory = DOMPurify as unknown as (\n\t\twin: Window & typeof globalThis,\n\t) => DOMPurifyInstance;\n\tpurifierInstance =\n\t\ttypeof factory === \"function\"\n\t\t\t? factory(window as Window & typeof globalThis)\n\t\t\t: (DOMPurify as unknown as DOMPurifyInstance);\n\treturn purifierInstance;\n}\n\n/**\n * Sanitize raw item/passage markup before it is injected into the DOM.\n *\n * - Strips `<script>`, event-handler attributes, unknown protocols and\n * a standard set of dangerous tags (`iframe`, `object`, `embed`, `base`,\n * `form`, `meta`, `link`).\n * - Preserves PIE custom elements (`pie-*`) and any extra tags listed in\n * `allowedCustomElements`.\n * - During SSR (no `window`) returns an empty string so untrusted markup\n * never reaches the prerender output; the live renderer will re-run the\n * sanitizer on hydrate.\n */\nexport function sanitizeItemMarkup(\n\tmarkup: string,\n\toptions: SanitizeItemMarkupOptions = {},\n): string {\n\tif (!markup) return \"\";\n\tconst purifier = resolvePurifier();\n\tif (!purifier) return \"\";\n\n\tconst allowedCustomElements = (options.allowedCustomElements ?? []).map(\n\t\t(name) => name.toLowerCase(),\n\t);\n\tconst explicitCustomElementSet = new Set(allowedCustomElements);\n\n\tconst result = purifier.sanitize(markup, {\n\t\tADD_TAGS: allowedCustomElements,\n\t\tADD_ATTR: BASE_ALLOWED_ATTRS,\n\t\tADD_URI_SAFE_ATTR: BASE_URI_SAFE_ATTRS,\n\t\tFORBID_TAGS: FORBIDDEN_TAGS,\n\t\tFORBID_ATTR: FORBIDDEN_ATTRS,\n\t\tALLOW_UNKNOWN_PROTOCOLS: false,\n\t\tSANITIZE_DOM: true,\n\t\t// pie-item contract compatibility: PIE models are matched to DOM\n\t\t// elements via strict `id` equality (see `updateSinglePieElement`\n\t\t// in players-shared/src/pie/updates.ts). `SANITIZE_NAMED_PROPS`\n\t\t// would prefix every `id`/`name` with `user-content-`, which silently\n\t\t// breaks model lookup for every item. `SANITIZE_DOM: true` above\n\t\t// still provides the core DOM-clobbering defenses we rely on.\n\t\tSANITIZE_NAMED_PROPS: false,\n\t\tWHOLE_DOCUMENT: false,\n\t\tALLOW_DATA_ATTR: true,\n\t\tALLOW_ARIA_ATTR: true,\n\t\tCUSTOM_ELEMENT_HANDLING: {\n\t\t\ttagNameCheck: (tagName: string) => {\n\t\t\t\tconst lower = tagName.toLowerCase();\n\t\t\t\treturn (\n\t\t\t\t\tPIE_CUSTOM_ELEMENT_REGEX.test(lower) ||\n\t\t\t\t\texplicitCustomElementSet.has(lower)\n\t\t\t\t);\n\t\t\t},\n\t\t\tattributeNameCheck: (attrName: string) =>\n\t\t\t\tCUSTOM_ELEMENT_ATTR_REGEX.test(attrName),\n\t\t\tallowCustomizedBuiltInElements: false,\n\t\t},\n\t\tRETURN_TRUSTED_TYPE: false,\n\t});\n\n\tconst sanitized = typeof result === \"string\" ? result : String(result ?? \"\");\n\t// PIE-94: wrap overwide authored images in a horizontal-scroll container\n\t// so they don't get clipped by ancestor `overflow-x: hidden` regions in\n\t// the section player (and match WCAG 1.4.10 Reflow at 400% zoom).\n\t// Tables get the same treatment so wide data grids reflow into a\n\t// scrollable region instead of forcing the page itself to scroll.\n\treturn wrapOverwideTables(wrapOverwideImages(sanitized));\n}\n\n/**\n * Build the default `ItemMarkupSanitizer` used by the players. The returned\n * function is stable for a given set of allowed custom elements so callers\n * can safely use reference equality when deciding whether to re-sanitize.\n */\nexport function createDefaultItemMarkupSanitizer(\n\toptions: SanitizeItemMarkupOptions = {},\n): ItemMarkupSanitizer {\n\tconst { allowedCustomElements } = options;\n\treturn (markup: string) =>\n\t\tsanitizeItemMarkup(markup, { allowedCustomElements });\n}\n\n/**\n * Derive the authoring-mode allow-list (`pie-*-config`) from a set of PIE\n * element tag names. Used by `transformMarkupForAuthoring` so the sanitizer\n * keeps the rewritten `-config` tags instead of stripping them.\n */\nexport function buildAuthoringAllowList(\n\telementTagNames: Iterable<string>,\n): string[] {\n\tconst out = new Set<string>();\n\tfor (const tag of elementTagNames) {\n\t\tif (!tag) continue;\n\t\tconst lower = tag.toLowerCase();\n\t\tout.add(lower);\n\t\tout.add(`${lower}-config`);\n\t}\n\treturn [...out];\n}\n\n/** Reset the memoised DOMPurify instance. Only intended for tests. */\nexport function resetPurifierForTesting() {\n\tpurifierInstance = null;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"sanitize-svg-icon.js","sourceRoot":"","sources":["../../src/security/sanitize-svg-icon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,SAAS,MAAM,WAAW,CAAC;AASlC,IAAI,mBAAmB,GAA6B,IAAI,CAAC;AAEzD,SAAS,kBAAkB;IAC1B,IAAI,mBAAmB;QAAE,OAAO,mBAAmB,CAAC;IACpD,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,CAAC,MAAM,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnE,MAAM,OAAO,GAAG,SAEM,CAAC;IACvB,mBAAmB;QAClB,OAAO,OAAO,KAAK,UAAU;YAC5B,CAAC,CAAC,OAAO,CAAC,MAAoC,CAAC;YAC/C,CAAC,CAAE,SAA0C,CAAC;IAChD,OAAO,mBAAmB,CAAC;AAC5B,CAAC;AAED,MAAM,cAAc,GAAG;IACtB,QAAQ;IACR,eAAe;IACf,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,MAAM;IACN,MAAM;CACN,CAAC;AAEF,MAAM,eAAe,GAAG;IACvB,SAAS;IACT,QAAQ;IACR,SAAS;IACT,aAAa;IACb,YAAY;IACZ,cAAc;IACd,cAAc;IACd,SAAS;IACT,QAAQ;IACR,WAAW;IACX,SAAS;IACT,YAAY;IACZ,YAAY;IACZ,YAAY;CACZ,CAAC;AAEF,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;AAC9C,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAE5B,MAAM,UAAU,eAAe,CAAC,IAAa;IAC5C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;IACjC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAC;IACzD,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IAExC,MAAM,QAAQ,GAAG,kBAAkB,EAAE,CAAC;IACtC,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE;QACtC,YAAY,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE;QAC7C,WAAW,EAAE,cAAc;QAC3B,WAAW,EAAE,eAAe;QAC5B,uBAAuB,EAAE,KAAK;QAC9B,mBAAmB,EAAE,KAAK;KAC1B,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC;IACvE,IAAI,WAAW,CAAC,IAAI,IAAI,gBAAgB,EAAE,CAAC;QAC1C,uEAAuE;QACvE,0DAA0D;QAC1D,MAAM,IAAI,GAAG,CAAC,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;QACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7B,CAAC;IACF,CAAC;IACD,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC3B,OAAO,GAAG,CAAC;AACZ,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,2BAA2B;IAC1C,mBAAmB,GAAG,IAAI,CAAC;IAC3B,WAAW,CAAC,KAAK,EAAE,CAAC;AACrB,CAAC","sourcesContent":["/**\n * Sanitize an SVG string intended to be rendered as an icon.\n *\n * Used by toolbars and tool-button components when their icon prop is an\n * inline SVG supplied by a tool configuration. Runs DOMPurify with the\n * SVG profile, forbids `<script>` / `<foreignObject>` and strips\n * event-handler attributes.\n *\n * Returns an empty string when `window` / `document` are unavailable (SSR)\n * or the input is not a string.\n */\n\nimport DOMPurify from \"dompurify\";\n\ninterface DOMPurifyInstance {\n\tsanitize: (\n\t\tsource: string,\n\t\tconfig?: Record<string, unknown>,\n\t) => string | Node | DocumentFragment;\n}\n\nlet svgPurifierInstance: DOMPurifyInstance | null = null;\n\nfunction resolveSvgPurifier(): DOMPurifyInstance | null {\n\tif (svgPurifierInstance) return svgPurifierInstance;\n\tif (typeof window === \"undefined\" || !window.document) return null;\n\tconst factory = DOMPurify as unknown as (\n\t\twin: Window & typeof globalThis,\n\t) => DOMPurifyInstance;\n\tsvgPurifierInstance =\n\t\ttypeof factory === \"function\"\n\t\t\t? factory(window as Window & typeof globalThis)\n\t\t\t: (DOMPurify as unknown as DOMPurifyInstance);\n\treturn svgPurifierInstance;\n}\n\nconst FORBIDDEN_TAGS = [\n\t\"script\",\n\t\"foreignobject\",\n\t\"iframe\",\n\t\"object\",\n\t\"embed\",\n\t\"base\",\n\t\"form\",\n];\n\nconst FORBIDDEN_ATTRS = [\n\t\"onerror\",\n\t\"onload\",\n\t\"onclick\",\n\t\"onmouseover\",\n\t\"onmouseout\",\n\t\"onmouseenter\",\n\t\"onmouseleave\",\n\t\"onfocus\",\n\t\"onblur\",\n\t\"onkeydown\",\n\t\"onkeyup\",\n\t\"onkeypress\",\n\t\"formaction\",\n\t\"xlink:href\",\n];\n\nconst stringCache = new Map<string, string>();\nconst STRING_CACHE_MAX = 64;\n\nexport function sanitizeSvgIcon(icon: unknown): string {\n\tif (typeof icon !== \"string\" || icon.length === 0) return \"\";\n\tconst trimmed = icon.trimStart();\n\tif (!trimmed.toLowerCase().startsWith(\"<svg\")) return \"\";\n\tconst cached = stringCache.get(icon);\n\tif (cached !== undefined) return cached;\n\n\tconst purifier = resolveSvgPurifier();\n\tif (!purifier) return \"\";\n\tconst result = purifier.sanitize(icon, {\n\t\tUSE_PROFILES: { svg: true, svgFilters: true },\n\t\tFORBID_TAGS: FORBIDDEN_TAGS,\n\t\tFORBID_ATTR: FORBIDDEN_ATTRS,\n\t\tALLOW_UNKNOWN_PROTOCOLS: false,\n\t\tRETURN_TRUSTED_TYPE: false,\n\t});\n\tconst str = typeof result === \"string\" ? result : String(result ?? \"\");\n\tif (stringCache.size >= STRING_CACHE_MAX) {\n\t\t// Naive LRU: clear oldest half when the cache fills up. Tool icons are\n\t\t// a small fixed set per assessment so this is rarely hit.\n\t\tconst keys = [...stringCache.keys()];\n\t\tfor (let i = 0; i < keys.length / 2; i += 1) {\n\t\t\tstringCache.delete(keys[i]);\n\t\t}\n\t}\n\tstringCache.set(icon, str);\n\treturn str;\n}\n\n/** Reset sanitizer state. Intended for tests. */\nexport function resetSvgSanitizerForTesting() {\n\tsvgPurifierInstance = null;\n\tstringCache.clear();\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"validate-style-url.js","sourceRoot":"","sources":["../../src/security/validate-style-url.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAsBH,MAAM,UAAU,wBAAwB,CACvC,GAAY,EACZ,UAAqC,EAAE;IAEvC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjD,OAAO;YACN,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,aAAa;YACrB,OAAO,EAAE,qDAAqD;SAC9D,CAAC;IACH,CAAC;IACD,IAAI,WAAgB,CAAC;IACrB,IAAI,CAAC;QACJ,WAAW,GAAG,OAAO,CAAC,OAAO;YAC5B,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC;YAC/B,CAAC,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACjB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,OAAO;YACN,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,aAAa;YACrB,OAAO,EAAE,gDAAgD,MAAM,CAAC,GAAG,CAAC,EAAE;SACtE,CAAC;IACH,CAAC;IACD,IAAI,WAAW,CAAC,QAAQ,KAAK,OAAO,IAAI,WAAW,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC3E,OAAO;YACN,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,qBAAqB;YAC7B,OAAO,EAAE,gCAAgC,WAAW,CAAC,QAAQ,oCAAoC;SACjG,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;IAC7C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACjE,OAAO;YACN,EAAE,EAAE,KAAK;YACT,MAAM,EAAE,mBAAmB;YAC3B,OAAO,EAAE,8BAA8B,WAAW,CAAC,MAAM,uCAAuC;SAChG,CAAC;IACH,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,GAAY;IACpD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAC3D,OAAO,GAAG;SACR,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;SAC5B,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACvC,CAAC","sourcesContent":["/**\n * Validate that an external stylesheet URL supplied via\n * `<pie-item-player external-style-urls=\"...\">` (or the\n * `itemConfig.resources.stylesheets[*].url` path) is safe to load.\n *\n * - Only `http:` / `https:` are allowed; `javascript:`, `data:`, `file:`\n * and custom schemes are rejected.\n * - When `allowedOrigins` is non-empty, the URL's origin must match one\n * of the listed origins. This lets hosts restrict style loading to a\n * known CDN allow-list.\n */\n\nexport type StyleUrlValidationOk = {\n\tok: true;\n\tresolvedUrl: URL;\n};\n\nexport type StyleUrlValidationError = {\n\tok: false;\n\treason: \"invalid-url\" | \"disallowed-protocol\" | \"disallowed-origin\";\n\tmessage: string;\n};\n\nexport type StyleUrlValidationResult =\n\t| StyleUrlValidationOk\n\t| StyleUrlValidationError;\n\nexport interface StyleUrlValidationOptions {\n\tbaseUrl?: string;\n\tallowedOrigins?: string[];\n}\n\nexport function validateExternalStyleUrl(\n\turl: unknown,\n\toptions: StyleUrlValidationOptions = {},\n): StyleUrlValidationResult {\n\tif (typeof url !== \"string\" || url.length === 0) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\treason: \"invalid-url\",\n\t\t\tmessage: \"External stylesheet URL must be a non-empty string.\",\n\t\t};\n\t}\n\tlet resolvedUrl: URL;\n\ttry {\n\t\tresolvedUrl = options.baseUrl\n\t\t\t? new URL(url, options.baseUrl)\n\t\t\t: new URL(url);\n\t} catch (err) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\treason: \"invalid-url\",\n\t\t\tmessage: `External stylesheet URL could not be parsed: ${String(err)}`,\n\t\t};\n\t}\n\tif (resolvedUrl.protocol !== \"http:\" && resolvedUrl.protocol !== \"https:\") {\n\t\treturn {\n\t\t\tok: false,\n\t\t\treason: \"disallowed-protocol\",\n\t\t\tmessage: `External stylesheet protocol ${resolvedUrl.protocol} is not allowed (only http/https).`,\n\t\t};\n\t}\n\tconst allowed = options.allowedOrigins ?? [];\n\tif (allowed.length > 0 && !allowed.includes(resolvedUrl.origin)) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\treason: \"disallowed-origin\",\n\t\t\tmessage: `External stylesheet origin ${resolvedUrl.origin} is not in the configured allow-list.`,\n\t\t};\n\t}\n\treturn { ok: true, resolvedUrl };\n}\n\nexport function parseAllowedStyleOrigins(raw: unknown): string[] {\n\tif (typeof raw !== \"string\" || raw.length === 0) return [];\n\treturn raw\n\t\t.split(\",\")\n\t\t.map((entry) => entry.trim())\n\t\t.filter((entry) => entry.length > 0);\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrap-model-rich-content.js","sourceRoot":"","sources":["../../src/security/wrap-model-rich-content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,sBAAsB,GAAG,mBAAmB,CAAC;AACnD,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC;AAExD,SAAS,oBAAoB,CAAC,KAAa;IAC1C,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtD,OAAO,kBAAkB,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC;AACtD,CAAC;AAED,SAAS,SAAS,CAAC,KAAc,EAAE,SAAkB;IACpD,IAAI,SAAS,IAAI,YAAY,CAAC,GAAG,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IAC3D,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAClE,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAEtD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YAChC,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;YACjC,IAAI,OAAO,KAAK,KAAK;gBAAE,OAAO,GAAG,IAAI,CAAC;YACtC,OAAO,OAAO,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,OAAO,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IAC/B,CAAC;IAED,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,IAAI,GAA4B,EAAE,CAAC;IACzC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACtC,IAAI,OAAO,KAAK,KAAK;YAAE,OAAO,GAAG,IAAI,CAAC;QACtC,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;IACrB,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAI,KAAQ;IAC/C,OAAO,SAAS,CAAC,KAAK,CAAM,CAAC;AAC9B,CAAC","sourcesContent":["import { wrapOverwideImages } from \"./wrap-overwide-images.js\";\nimport { wrapOverwideTables } from \"./wrap-overwide-tables.js\";\n\nconst RICH_CONTENT_TAG_REGEX = /<(?:img|table)\\b/i;\nconst SKIPPED_KEYS = new Set([\"accessibilityCatalogs\"]);\n\nfunction maybeWrapRichContent(value: string): string {\n\tif (!RICH_CONTENT_TAG_REGEX.test(value)) return value;\n\treturn wrapOverwideTables(wrapOverwideImages(value));\n}\n\nfunction wrapValue(value: unknown, parentKey?: string): unknown {\n\tif (parentKey && SKIPPED_KEYS.has(parentKey)) return value;\n\tif (typeof value === \"string\") return maybeWrapRichContent(value);\n\tif (!value || typeof value !== \"object\") return value;\n\n\tif (Array.isArray(value)) {\n\t\tlet changed = false;\n\t\tconst next = value.map((entry) => {\n\t\t\tconst wrapped = wrapValue(entry);\n\t\t\tif (wrapped !== entry) changed = true;\n\t\t\treturn wrapped;\n\t\t});\n\t\treturn changed ? next : value;\n\t}\n\n\tconst record = value as Record<string, unknown>;\n\tlet changed = false;\n\tconst next: Record<string, unknown> = {};\n\tfor (const [key, entry] of Object.entries(record)) {\n\t\tconst wrapped = wrapValue(entry, key);\n\t\tif (wrapped !== entry) changed = true;\n\t\tnext[key] = wrapped;\n\t}\n\treturn changed ? next : value;\n}\n\nexport function wrapModelRichContent<T>(model: T): T {\n\treturn wrapValue(model) as T;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrap-overwide-images.js","sourceRoot":"","sources":["../../src/security/wrap-overwide-images.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Wraps authored `<img>` elements with a horizontally scrollable container so\n * images that are wider than their column surface a scrollbar instead of being\n * clipped by ancestor `overflow-x: hidden` regions (PIE-94).\n *\n * The wrapper is rendered as\n * `<span class=\"pie-image-scroll\" tabindex=\"0\" role=\"region\" aria-label=\"...\">`\n * and receives the accompanying CSS from `@pie-players/pie-theme`. The CSS uses\n * `overflow-x: auto` so small images stay visually unchanged: a scrollbar only\n * appears when the image's intrinsic width exceeds the wrapper's available\n * space (including at higher browser-zoom levels, which is the original driver\n * for this change — WCAG 1.4.10 Reflow at 400% zoom).\n *\n * Two callable surfaces share the same wrapping logic:\n *\n * - `wrapOverwideImages(markup)` — string-in / string-out, used as a\n * post-sanitization step inside `sanitizeItemMarkup`. By design it skips\n * images inside `pie-*` elements; those are the element's own template and\n * should not be restructured by the authored-markup pipeline.\n * - `wrapOverwideImagesInElement(root)` — operates on a live DOM subtree.\n * Used by the post-render pass in `PieItemPlayer.svelte` so that images a\n * PIE element paints into its own light DOM (e.g. a `pie-passage`'s\n * model-driven content) get the same scrollable affordance even though\n * they never appeared in the authored markup string.\n */\n\nconst SCROLL_WRAPPER_CLASS = \"pie-image-scroll\";\nconst PIE_CUSTOM_ELEMENT_TAG_REGEX = /^pie-/i;\n\nfunction isInsidePieCustomElement(image: Element, root: Element): boolean {\n\tlet ancestor: Element | null = image.parentElement;\n\twhile (ancestor && ancestor !== root) {\n\t\tif (PIE_CUSTOM_ELEMENT_TAG_REGEX.test(ancestor.tagName)) {\n\t\t\treturn true;\n\t\t}\n\t\tancestor = ancestor.parentElement;\n\t}\n\treturn false;\n}\n\nfunction buildAriaLabel(image: Element): string {\n\tconst alt = image.getAttribute(\"alt\");\n\tconst trimmed = alt ? alt.trim() : \"\";\n\treturn trimmed ? `Scrollable image: ${trimmed}` : \"Scrollable image\";\n}\n\nexport interface WrapOverwideImagesInElementOptions {\n\t/**\n\t * When `true`, images whose nearest `pie-*` ancestor is *strictly between*\n\t * the image and `root` are left alone. Used by the string pipeline so the\n\t * authored-markup pass doesn't restructure a PIE element's own template.\n\t * Defaults to `false` so the live-DOM pass *does* wrap element-rendered\n\t * images.\n\t */\n\tskipPieDescendants?: boolean;\n}\n\n/**\n * Wrap every unwrapped `<img>` descendant of `root` with the shared\n * horizontal-scroll span. Returns the number of newly-wrapped images so\n * callers can short-circuit when nothing changed. Idempotent.\n */\nexport function wrapOverwideImagesInElement(\n\troot: Element,\n\toptions: WrapOverwideImagesInElementOptions = {},\n): number {\n\tconst { skipPieDescendants = false } = options;\n\tconst images = Array.from(root.querySelectorAll(\"img\"));\n\tif (images.length === 0) return 0;\n\n\tconst ownerDocument = root.ownerDocument;\n\tif (!ownerDocument) return 0;\n\n\tlet wrapped = 0;\n\tfor (const image of images) {\n\t\tconst parent = image.parentElement;\n\t\tif (!parent) continue;\n\n\t\t// Idempotency — already wrapped.\n\t\tif (parent.classList && parent.classList.contains(SCROLL_WRAPPER_CLASS)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Authored-markup pass: leave PIE custom-element internals alone.\n\t\tif (skipPieDescendants && isInsidePieCustomElement(image, root)) continue;\n\n\t\tconst wrapper = ownerDocument.createElement(\"span\");\n\t\twrapper.className = SCROLL_WRAPPER_CLASS;\n\t\twrapper.setAttribute(\"tabindex\", \"0\");\n\t\twrapper.setAttribute(\"role\", \"region\");\n\t\twrapper.setAttribute(\"aria-label\", buildAriaLabel(image));\n\n\t\tparent.insertBefore(wrapper, image);\n\t\twrapper.appendChild(image);\n\t\twrapped += 1;\n\t}\n\treturn wrapped;\n}\n\n/**\n * Wrap `<img>` elements in `markup` with a horizontal-scroll container.\n *\n * - No-ops on empty input.\n * - No-ops during SSR (no `window` / `DOMParser`) — the markup is returned\n * unchanged; the browser re-run on hydrate will perform the wrap.\n * - Idempotent: images whose direct parent already carries the\n * `pie-image-scroll` class are left alone.\n * - Leaves images inside PIE custom elements (`<pie-*>`) alone. Those are\n * rendered by the element's own template / shadow DOM and should not be\n * restructured by the authored-markup pipeline. Use\n * `wrapOverwideImagesInElement` for the post-render pass that *does* want\n * to wrap element-rendered images.\n */\nexport function wrapOverwideImages(markup: string): string {\n\tif (!markup) return \"\";\n\n\t// Fast path: avoid the DOM round-trip entirely when the markup carries no\n\t// images. Keeps the sanitize pipeline cheap for the common case.\n\tif (!/<img\\b/i.test(markup)) return markup;\n\n\tif (typeof window === \"undefined\" || !window.document) return markup;\n\n\tconst ParserCtor =\n\t\ttypeof DOMParser !== \"undefined\"\n\t\t\t? DOMParser\n\t\t\t: (window as unknown as { DOMParser?: typeof DOMParser }).DOMParser;\n\tif (!ParserCtor) return markup;\n\n\tconst doc = new ParserCtor().parseFromString(\n\t\t`<!DOCTYPE html><html><body>${markup}</body></html>`,\n\t\t\"text/html\",\n\t);\n\tconst body = doc.body;\n\tif (!body) return markup;\n\n\tconst wrapped = wrapOverwideImagesInElement(body, {\n\t\tskipPieDescendants: true,\n\t});\n\treturn wrapped > 0 ? body.innerHTML : markup;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrap-overwide-tables.js","sourceRoot":"","sources":["../../src/security/wrap-overwide-tables.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Wraps authored `<table>` elements with a horizontally scrollable container so\n * tables that are wider than their column surface a scrollbar instead of being\n * clipped by ancestor `overflow-x: hidden` regions.\n *\n * The wrapper is rendered as\n * `<div class=\"pie-table-scroll\" tabindex=\"0\" role=\"region\" aria-label=\"...\">`\n * and receives the accompanying CSS from `@pie-players/pie-theme`. The CSS uses\n * `overflow-x: auto` so narrow tables stay visually unchanged: a scrollbar only\n * appears when the table's intrinsic width exceeds the wrapper's available\n * space (including at higher browser-zoom levels — WCAG 1.4.10 Reflow at 400%\n * zoom is the same driver as for `wrapOverwideImages`).\n *\n * Two callable surfaces share the same wrapping logic:\n *\n * - `wrapOverwideTables(markup)` — string-in / string-out, used as a\n * post-sanitization step inside `sanitizeItemMarkup`. By design it skips\n * tables inside `pie-*` elements; those are the element's own template and\n * should not be restructured by the authored-markup pipeline.\n * - `wrapOverwideTablesInElement(root)` — operates on a live DOM subtree.\n * Used by the post-render pass in `PieItemPlayer.svelte` so that tables a\n * PIE element paints into its own light DOM (e.g. a `pie-passage`'s\n * model-driven content) get the same scrollable affordance even though\n * they never appeared in the authored markup string.\n */\n\nconst SCROLL_WRAPPER_CLASS = \"pie-table-scroll\";\nconst PIE_CUSTOM_ELEMENT_TAG_REGEX = /^pie-/i;\n\nfunction isInsidePieCustomElement(table: Element, root: Element): boolean {\n\tlet ancestor: Element | null = table.parentElement;\n\twhile (ancestor && ancestor !== root) {\n\t\tif (PIE_CUSTOM_ELEMENT_TAG_REGEX.test(ancestor.tagName)) {\n\t\t\treturn true;\n\t\t}\n\t\tancestor = ancestor.parentElement;\n\t}\n\treturn false;\n}\n\nfunction buildAriaLabel(table: Element): string {\n\t// Authors commonly label tables via <caption>, aria-label, or aria-labelledby.\n\t// Prefer the most explicit signal and fall back to the generic label so\n\t// every wrapper still announces itself as a region.\n\tconst ariaLabel = table.getAttribute(\"aria-label\");\n\tif (ariaLabel?.trim()) {\n\t\treturn `Scrollable table: ${ariaLabel.trim()}`;\n\t}\n\tconst labelledBy = table.getAttribute(\"aria-labelledby\");\n\tif (labelledBy?.trim()) {\n\t\tconst ownerDocument = table.ownerDocument;\n\t\tconst ids = labelledBy.trim().split(/\\s+/);\n\t\tconst labels: string[] = [];\n\t\tfor (const id of ids) {\n\t\t\tconst labelEl = ownerDocument?.getElementById(id);\n\t\t\tconst text = labelEl?.textContent?.trim();\n\t\t\tif (text) labels.push(text);\n\t\t}\n\t\tif (labels.length > 0) {\n\t\t\treturn `Scrollable table: ${labels.join(\" \")}`;\n\t\t}\n\t}\n\tconst caption = table.querySelector(\"caption\");\n\tconst captionText = caption?.textContent?.trim();\n\tif (captionText) {\n\t\treturn `Scrollable table: ${captionText}`;\n\t}\n\treturn \"Scrollable table\";\n}\n\nexport interface WrapOverwideTablesInElementOptions {\n\t/**\n\t * When `true`, tables whose nearest `pie-*` ancestor is *strictly between*\n\t * the table and `root` are left alone. Used by the string pipeline so the\n\t * authored-markup pass doesn't restructure a PIE element's own template.\n\t * Defaults to `false` so the live-DOM pass *does* wrap element-rendered\n\t * tables.\n\t */\n\tskipPieDescendants?: boolean;\n}\n\n/**\n * Wrap every unwrapped `<table>` descendant of `root` with the shared\n * horizontal-scroll div. Returns the number of newly-wrapped tables so\n * callers can short-circuit when nothing changed. Idempotent.\n */\nexport function wrapOverwideTablesInElement(\n\troot: Element,\n\toptions: WrapOverwideTablesInElementOptions = {},\n): number {\n\tconst { skipPieDescendants = false } = options;\n\tconst tables = Array.from(root.querySelectorAll(\"table\"));\n\tif (tables.length === 0) return 0;\n\n\tconst ownerDocument = root.ownerDocument;\n\tif (!ownerDocument) return 0;\n\n\tlet wrapped = 0;\n\tfor (const table of tables) {\n\t\tconst parent = table.parentElement;\n\t\tif (!parent) continue;\n\n\t\t// Idempotency — already wrapped.\n\t\tif (parent.classList && parent.classList.contains(SCROLL_WRAPPER_CLASS)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Authored-markup pass: leave PIE custom-element internals alone.\n\t\tif (skipPieDescendants && isInsidePieCustomElement(table, root)) continue;\n\n\t\tconst wrapper = ownerDocument.createElement(\"div\");\n\t\twrapper.className = SCROLL_WRAPPER_CLASS;\n\t\twrapper.setAttribute(\"tabindex\", \"0\");\n\t\twrapper.setAttribute(\"role\", \"region\");\n\t\twrapper.setAttribute(\"aria-label\", buildAriaLabel(table));\n\n\t\tparent.insertBefore(wrapper, table);\n\t\twrapper.appendChild(table);\n\t\twrapped += 1;\n\t}\n\treturn wrapped;\n}\n\nexport function wrapOverwideTables(markup: string): string {\n\tif (!markup) return \"\";\n\n\t// Fast path: avoid the DOM round-trip entirely when the markup carries no\n\t// tables. Keeps the sanitize pipeline cheap for the common case.\n\tif (!/<table\\b/i.test(markup)) return markup;\n\n\tif (typeof window === \"undefined\" || !window.document) return markup;\n\n\tconst ParserCtor =\n\t\ttypeof DOMParser !== \"undefined\"\n\t\t\t? DOMParser\n\t\t\t: (window as unknown as { DOMParser?: typeof DOMParser }).DOMParser;\n\tif (!ParserCtor) return markup;\n\n\tconst doc = new ParserCtor().parseFromString(\n\t\t`<!DOCTYPE html><html><body>${markup}</body></html>`,\n\t\t\"text/html\",\n\t);\n\tconst body = doc.body;\n\tif (!body) return markup;\n\n\tconst wrapped = wrapOverwideTablesInElement(body, {\n\t\tskipPieDescendants: true,\n\t});\n\treturn wrapped > 0 ? body.innerHTML : markup;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"npm-registry.js","sourceRoot":"","sources":["../../src/server/npm-registry.ts"],"names":[],"mappings":"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","sourcesContent":["import semver from \"semver\";\n\nconst NPM_REGISTRY_URL = \"https://registry.npmjs.org\";\n\nif (typeof window !== \"undefined\") {\n\tthrow new Error(\n\t\t\"[npm-registry] @pie-players/pie-players-shared/server/npm-registry is server-only and cannot be imported in browser code\",\n\t);\n}\n\nconst DEFAULT_FILTER = (v: string) =>\n\tv.indexOf(\"print\") === -1 &&\n\tv.indexOf(\"ps\") === -1 &&\n\tv.indexOf(\"strikethrough\") === -1 &&\n\tv.indexOf(\"alpha\") === -1 &&\n\tv.indexOf(\"bm\") === -1;\n\ntype NpmFetch = (\n\tinput: RequestInfo | URL,\n\tinit?: RequestInit,\n) => Promise<Response>;\n\n/**\n * Server-only helper for npm package version lookups.\n * Do not import this module from browser-executed code.\n */\nexport const getNpmPackageVersions = async (\n\telement: string,\n\tfetch: NpmFetch,\n\tfilter: (v: string) => boolean = DEFAULT_FILTER,\n\tlimit?: number,\n\tsearchTerm = \"\",\n): Promise<string[]> => {\n\tif (typeof window !== \"undefined\") {\n\t\tthrow new Error(\n\t\t\t\"[npm-registry] server-only module imported in browser context\",\n\t\t);\n\t}\n\n\ttry {\n\t\tconst response = await fetch(`${NPM_REGISTRY_URL}/${element}`);\n\t\tif (!response.ok) {\n\t\t\tconsole.warn(\n\t\t\t\t`[npm-registry] npm status ${response.status} for package ${element}`,\n\t\t\t);\n\t\t\treturn searchTerm ? [] : [\"latest\"];\n\t\t}\n\n\t\tconst json = await response.json();\n\t\tconst versions = json?.versions as Record<\n\t\t\tstring,\n\t\t\t{ deprecated?: string }\n\t\t> | null;\n\t\tif (!versions || typeof versions !== \"object\") {\n\t\t\tconsole.warn(\n\t\t\t\t`[npm-registry] response for ${element} missing 'versions' property`,\n\t\t\t);\n\t\t\treturn searchTerm ? [] : [\"latest\"];\n\t\t}\n\n\t\tlet sorted = Object.keys(versions)\n\t\t\t.filter(filter)\n\t\t\t.filter((v) => !versions[v]?.deprecated)\n\t\t\t.filter((v) => !/^11\\.0\\.\\d+-esm\\.\\d+/.test(v))\n\t\t\t.filter(\n\t\t\t\t(v) =>\n\t\t\t\t\t!searchTerm || v.toLowerCase().includes(searchTerm.toLowerCase()),\n\t\t\t)\n\t\t\t.sort((a, b) => {\n\t\t\t\tconst verA = semver.valid(semver.clean(a));\n\t\t\t\tconst verB = semver.valid(semver.clean(b));\n\t\t\t\tif (!verA && !verB) return 0;\n\t\t\t\tif (!verA) return 1;\n\t\t\t\tif (!verB) return -1;\n\t\t\t\treturn semver.rcompare(verA, verB);\n\t\t\t});\n\n\t\tif (typeof limit === \"number\" && limit > 0) {\n\t\t\tsorted = sorted.slice(0, limit);\n\t\t}\n\n\t\tif (!searchTerm) {\n\t\t\tsorted.unshift(\"latest\");\n\t\t}\n\n\t\treturn sorted;\n\t} catch (error) {\n\t\tconsole.warn(`[npm-registry] failed package lookup for ${element}:`, error);\n\t\treturn searchTerm ? [] : [\"latest\"];\n\t}\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * This contains copies of the data types we use in PIEOneer. These types, unlike\n * the ones in the datastore package, should not have any dependencies, and might in some\n * cases be more shallow than their originals. The main reason for these is that they should\n * be able to be used in front-end code (as well as the PIEOneer backend code).\n *\n * INCLUDING REFERENCES TO THE DATASTORE PACKAGE INSTEAD WILL MAKE PIEONEER THROW 500\n * EVERYWHERE WITHOUT ANY FURTHER EXPLANATION\n */\nexport interface BaseEntity {\n\tid?: string;\n\tcreatedAt?: Date;\n\tupdatedAt?: Date;\n}\n\nexport interface CollectionEntity extends BaseEntity {\n\tname: string;\n\torganization: string | OrganizationEntity;\n}\n\nexport interface OrganizationEntity extends BaseEntity {\n\tname: string;\n\tdefaultCollection?: string | CollectionEntity;\n}\n\nexport interface UserEntity extends BaseEntity {\n\temail: string;\n\tname?: string;\n}\n\nexport interface ClientEntity extends BaseEntity {\n\tname: string;\n\tsecret: string;\n\tuser: string | UserEntity;\n\torganization: string | OrganizationEntity;\n}\n\nexport interface AssignmentEntity extends BaseEntity {\n\torganization: string | OrganizationEntity;\n}\n\nexport interface SessionEntity extends BaseEntity {\n\titem: ItemEntity;\n\tassignment?: string | AssignmentEntity; // TODO: Remove after data is migrated\n\torganization?: string | OrganizationEntity;\n}\n\nexport enum SessionEventType {\n\tSAVE = \"save\",\n\tMODEL = \"model\",\n\tSCORE = \"score\",\n\tMANUAL_SCORE = \"manual-score\",\n}\n\nexport enum ScoreType {\n\tAUTO = \"auto\",\n\tMANUAL = \"manual\",\n}\n\nexport interface SessionScore {\n\tpoints?: number;\n\tmax?: number;\n\ttype?: ScoreType;\n\tpartialScoring?: boolean;\n\tmessage?: string;\n\terrors?: string[];\n}\n\nexport interface SessionAutoScore extends SessionScore {\n\telements?: any[];\n\textraProps?: any;\n}\n\nexport interface SessionManualScore extends SessionScore {}\n\nexport interface SessionEventEntity extends BaseEntity {\n\tsession: string | SessionEntity;\n\ttype?: SessionEventType;\n\tsaveEventId?: string;\n\tscore?: number;\n}\n\nexport enum ModeType {\n\tGATHER = \"gather\",\n\tVIEW = \"view\",\n\tEVALUATE = \"evaluate\",\n}\n\nexport interface FlatSession {\n\tid: string;\n\tassignment: AssignmentEntity;\n\titem: string;\n\tdata: any[];\n\tcreatedAt: Date;\n\tupdatedAt: Date;\n\tmode: ModeType;\n\tautoScore: SessionAutoScore;\n\tmanualScore: SessionManualScore;\n\tevents: SessionEventEntity[]; // raw events;\n}\n\nexport interface SemVerPrerelease {\n\ttag: string;\n\tversion: number;\n}\n\nexport interface SemVer {\n\tmajor: number;\n\tminor: number;\n\tpatch: number;\n\tprerelease?: SemVerPrerelease;\n}\n\nexport interface VersionEntity extends BaseEntity {\n\tbaseId: string;\n\tsignature?: string;\n\tversion: SemVer;\n}\n\nexport interface SearchMetaData {\n\t[key: string]: any;\n}\n\nexport interface SearchMetaDataEntity extends BaseEntity {\n\tsearchMetaData?: SearchMetaData;\n}\n\nexport interface ConfigElements {\n\t[key: string]: string;\n}\n\nexport interface PieModel {\n\tid: string;\n\telement: string;\n\t/**\n\t * QTI/APIP-style accessibility catalogs owned by this model's renderable fields\n\t * (prompt, choices, feedback, rationale, etc.).\n\t */\n\taccessibilityCatalogs?: AccessibilityCatalog[];\n\n\t[key: string]: any;\n}\n\nexport interface ConfigEntity {\n\tid?: string;\n\tmarkup: string;\n\telements: ConfigElements;\n\tmodels: PieModel[];\n\tconfiguration?: Record<string, any>; // NEW: Configure settings for authoring mode\n\n\t/**\n\t * QTI 3.0: Extracted accessibility catalogs from embedded SSML.\n\t * Automatically generated by SSMLExtractor when <speak> tags are found in content.\n\t */\n\textractedCatalogs?: AccessibilityCatalog[];\n\n\t[key: string]: any;\n}\n\nexport interface ConfigContainerEntity extends BaseEntity {\n\tconfig: ConfigEntity;\n}\n\nexport interface PassageEntity\n\textends VersionEntity,\n\t\tConfigContainerEntity,\n\t\tSearchMetaDataEntity {\n\tname: string;\n\t/** QTI/APIP-style accessibility catalogs owned by this passage content. */\n\taccessibilityCatalogs?: AccessibilityCatalog[];\n\tretired?: boolean;\n\tpublished?: boolean;\n\t// Search result highlights from OpenSearch (keyed by field name)\n\thighlights?: Record<string, string[]>;\n}\n\nexport interface ItemEntity\n\textends VersionEntity,\n\t\tConfigContainerEntity,\n\t\tSearchMetaDataEntity {\n\tname?: string;\n\tpassage?: string | PassageEntity;\n\t/** QTI/APIP-style accessibility catalogs owned by the item root. */\n\taccessibilityCatalogs?: AccessibilityCatalog[];\n\tretired?: boolean;\n\tpublished?: boolean;\n\tconfiguration?: any;\n\t// Search result highlights from OpenSearch (keyed by field name)\n\thighlights?: Record<string, string[]>;\n}\n\nexport interface AssignmentSessionData {\n\tassignment: AssignmentEntity;\n\tsessions: FlatSession[];\n\titems: ItemEntity[];\n\terror?: string;\n}\n\n/**\n * Metadata specifically for interpretation by clients, typically containing\n * options that are relevant for the user interface. This is not indexed for search.\n */\nexport interface SettingsMetaData {\n\t[key: string]: any;\n}\n\n/**\n * Objects that contain metadata in the settings property, which is NOT indexed for search\n * but typically only relevant for particular client cases.\n */\nexport interface SettingsMetaDataEntity {\n\tsettings?: SettingsMetaData;\n}\n\n/**\n * A reference to an item with optionally a title, settings and metadata.\n */\nexport interface QuestionEntity\n\textends SearchMetaDataEntity,\n\t\tSettingsMetaDataEntity {\n\tid?: string;\n\ttitle?: string;\n\titemVId: string;\n\titem?: ItemEntity;\n}\n\n// ============================================================================\n// QTI-aligned assessment model (QTI 2.x compatible shape)\n// ============================================================================\n\nexport type RubricBlockView =\n\t| \"author\"\n\t| \"candidate\"\n\t| \"proctor\"\n\t| \"scorer\"\n\t| \"testConstructor\"\n\t| \"tutor\";\n\nexport interface RubricBlock {\n\tidentifier?: string;\n\t/** QTI spec: space-separated list of roles; stored as an array */\n\tview: RubricBlockView[];\n\tclass?: \"stimulus\" | \"instructions\" | \"rubric\";\n\n\t/**\n\t * Versioned ID reference to the passage (when class=\"stimulus\").\n\t */\n\tpassageVId?: string;\n\n\t/**\n\t * Embedded passage entity (PIE-native approach).\n\t * The passage is rendered using the same ItemPlayer infrastructure as items.\n\t * Contains a PIE config with markup, elements, and models.\n\t */\n\tpassage?: PassageEntity;\n\n\t/**\n\t * Simple HTML content string (for basic use cases).\n\t * Alternative to the passage entity approach.\n\t */\n\tcontent?: string;\n}\n\n/**\n * QTI-aligned assessment item reference (maps to qti-assessment-item-ref).\n * References an item within a section. The item property contains the resolved\n * ItemEntity with its PIE config.\n */\nexport interface AssessmentItemRef extends SearchMetaDataEntity {\n\tid?: string;\n\tidentifier: string;\n\ttitle?: string;\n\trequired?: boolean;\n\n\t/**\n\t * Item virtual ID - stable identifier for the item across versions\n\t */\n\titemVId?: string;\n\n\t/**\n\t * Resolved item entity with PIE config.\n\t * This is populated by the client before passing to the player.\n\t */\n\titem?: ItemEntity;\n\n\t/** Item-level settings for tool requirements and customization */\n\tsettings?: ItemSettings;\n}\n\nexport interface AssessmentSection\n\textends SearchMetaDataEntity,\n\t\tSettingsMetaDataEntity {\n\tid?: string;\n\tidentifier: string;\n\ttitle?: string;\n\tvisible?: boolean;\n\trequired?: boolean;\n\t/**\n\t * QTI 3 pagination hint (`qti-assessment-section@keep-together`).\n\t *\n\t * Indicates that the delivery system SHOULD render this section's items\n\t * together rather than splitting them across pages. This is strictly a\n\t * rendering/layout hint — it does NOT disable item-level navigation,\n\t * current-item tracking, or `item-selected` events in the section player.\n\t * Paginated and keep-together sections both support Next/Back,\n\t * `getCurrentItem()`, and `item-selected` events.\n\t */\n\tkeepTogether?: boolean;\n\n\tsections?: AssessmentSection[];\n\n\t/**\n\t * QTI 3.0: Assessment item references (items in this section).\n\t * Maps to qti-assessment-item-ref in QTI 3.0 XML.\n\t */\n\tassessmentItemRefs?: AssessmentItemRef[];\n\n\t// Shared context (passages/instructions/rubrics) for this section\n\trubricBlocks?: RubricBlock[];\n\n\tsort?: string;\n}\n\nexport interface TestPart {\n\tid?: string;\n\tidentifier: string;\n\tnavigationMode: \"linear\" | \"nonlinear\";\n\tsubmissionMode: \"individual\" | \"simultaneous\";\n\tsections: AssessmentSection[];\n}\n\n// ============================================================================\n// QTI 3.0 Types\n// ============================================================================\n\nexport interface ContextDeclaration {\n\tidentifier: string;\n\tbaseType:\n\t\t| \"boolean\"\n\t\t| \"integer\"\n\t\t| \"float\"\n\t\t| \"string\"\n\t\t| \"identifier\"\n\t\t| \"point\"\n\t\t| \"pair\"\n\t\t| \"directedPair\"\n\t\t| \"duration\"\n\t\t| \"file\"\n\t\t| \"uri\";\n\tcardinality: \"single\" | \"multiple\" | \"ordered\" | \"record\";\n\tdefaultValue?: any;\n}\n\nexport interface CatalogCard {\n\tcatalog: string; // 'spoken', 'sign-language', 'braille', etc.\n\tlanguage?: string;\n\tcontent: string;\n}\n\nexport interface AccessibilityCatalog {\n\tidentifier: string;\n\tcards: CatalogCard[];\n}\n\nexport interface PersonalNeedsProfile {\n\tsupports: string[];\n\tprohibitedSupports?: string[];\n\tactivateAtInit?: string[];\n}\n\n/**\n * QTI 3.0: Reference to a shared stimulus (passage).\n * Maps to qti-assessment-stimulus-ref element.\n *\n * NOTE: Not currently used. We embed PassageEntity directly in RubricBlock instead.\n * Kept for potential future use if we need to support external stimulus references.\n */\n/*\nexport interface StimulusRef {\n\tidentifier: string;\n\thref: string;\n}\n*/\n\nexport interface AssessmentEntity extends BaseEntity, SearchMetaDataEntity {\n\tname?: string;\n\ttitle?: string;\n\tidentifier?: string;\n\tdescription?: string;\n\n\t/** Enhanced structured settings for assessment configuration */\n\tsettings?: AssessmentSettings;\n\n\t/** QTI version - '3.0' for QTI 3.0 format */\n\tqtiVersion?: \"3.0\";\n\n\t/** QTI 3.0: Context declarations (global shared variables) */\n\tcontextDeclarations?: ContextDeclaration[];\n\n\t/** QTI 3.0: Integrated APIP accessibility catalogs */\n\taccessibilityCatalogs?: AccessibilityCatalog[];\n\n\t/** QTI 3.0: Personal Needs Profile (PNP 3.0) */\n\tpersonalNeedsProfile?: PersonalNeedsProfile;\n\n\t/**\n\t * QTI 3.0: testParts structure (authoritative for QTI format).\n\t */\n\ttestParts?: TestPart[];\n\n\t/** QTI 3.0: Stimulus material references (Phase 3 - placeholder) */\n\tstimulusRefs?: any[];\n}\n\n/**\n * Enhanced settings structure for assessment configuration.\n * Provides structured fields for district policies, test administration,\n * tool configurations, and theme settings while remaining extensible.\n */\nexport interface AssessmentSettings {\n\t/** District/organization policies */\n\tdistrictPolicy?: {\n\t\tblockedTools?: string[]; // PNP support IDs that are blocked\n\t\trequiredTools?: string[]; // PNP support IDs that are required\n\t\tpolicies?: Record<string, any>;\n\t};\n\n\t/** Test administration configuration */\n\ttestAdministration?: {\n\t\tmode?: \"practice\" | \"test\" | \"benchmark\";\n\t\ttoolOverrides?: Record<string, boolean>; // Override specific PNP supports\n\t\tstartDate?: string;\n\t\tendDate?: string;\n\t};\n\n\t/** Tool-specific provider configurations */\n\ttoolConfigs?: {\n\t\t// Calculator-specific options are owned by the calculator tool package.\n\t\tcalculator?: AssessmentCalculatorConfig;\n\t\t/**\n\t\t * Text-to-speech configuration.\n\t\t *\n\t\t * Standard parameters (voice, rate, pitch) are portable across providers\n\t\t * and follow W3C Web Speech API specifications.\n\t\t *\n\t\t * Provider-specific extensions should be placed in providerOptions.\n\t\t *\n\t\t * @see https://w3c.github.io/speech-api/\n\t\t */\n\t\ttextToSpeech?: {\n\t\t\t/**\n\t\t\t * TTS provider\n\t\t\t *\n\t\t\t * @standard \"browser\" uses W3C Web Speech API\n\t\t\t * @extension \"polly\" and \"custom\" are provider-specific\n\t\t\t */\n\t\t\tprovider?: \"browser\" | \"polly\" | \"custom\";\n\n\t\t\t/**\n\t\t\t * Voice identifier (provider-specific names)\n\t\t\t *\n\t\t\t * @standard W3C Web Speech API (concept)\n\t\t\t * @example \"Joanna\" (Polly), \"en-US-Standard-A\" (Google), browser voices\n\t\t\t */\n\t\t\tvoice?: string;\n\n\t\t\t/**\n\t\t\t * Speech rate (speed multiplier)\n\t\t\t *\n\t\t\t * @standard W3C Web Speech API\n\t\t\t * @range 0.25 to 4.0\n\t\t\t * @default 1.0\n\t\t\t */\n\t\t\trate?: number;\n\n\t\t\t/**\n\t\t\t * Pitch adjustment\n\t\t\t *\n\t\t\t * @standard W3C Web Speech API\n\t\t\t * @range 0 to 2 (as multiplier)\n\t\t\t * @default 1.0\n\t\t\t */\n\t\t\tpitch?: number;\n\n\t\t\t/**\n\t\t\t * Speech Rule Engine options for generated MathML speech.\n\t\t\t *\n\t\t\t * `style` is passed to SRE directly; ClearSpeak combines multiple\n\t\t\t * preferences with \":\" (for example,\n\t\t\t * \"ImpliedTimes_MoreImpliedTimes:Paren_Silent\").\n\t\t\t *\n\t\t\t * Mirrors assessment-toolkit's SREMathSpeechOptions without importing\n\t\t\t * toolkit service types into the shared content contract package.\n\t\t\t */\n\t\t\tmathSpeech?: {\n\t\t\t\tdomain?: string;\n\t\t\t\tstyle?: string;\n\t\t\t\tengineOptions?: Record<string, unknown>;\n\t\t\t};\n\n\t\t\t/**\n\t\t\t * Provider-specific options (extensions)\n\t\t\t *\n\t\t\t * @extension Not portable across providers\n\t\t\t * @example For AWS Polly: { engine: \"neural\", region: \"us-east-1\" }\n\t\t\t * @example For Google Cloud: { audioEncoding: \"MP3\", effectsProfileId: [\"headphone-class-device\"] }\n\t\t\t */\n\t\t\tproviderOptions?: Record<string, any>;\n\t\t};\n\t\t[toolId: string]: any; // Other tool configs\n\t};\n\n\t/** Theme configuration (not in PNP) */\n\tthemeConfig?: {\n\t\tscheme?: \"default\" | \"high-contrast\" | \"dark\";\n\t\tfontSize?: number;\n\t\tfontFamily?: string;\n\t\tlineHeight?: number;\n\t\treducedMotion?: boolean;\n\t};\n\n\t/** Product-specific extensions */\n\t[key: string]: any;\n}\n\n/**\n * Calculator options exposed through assessment settings.\n * The engine is implicit (Desmos).\n */\nexport interface AssessmentCalculatorConfig {\n\ttype?: \"basic\" | \"scientific\" | \"graphing\";\n\t[key: string]: any;\n}\n\n/**\n * Item-level settings for tool requirements.\n * Used in AssessmentItemRef.settings to specify item-specific tool needs.\n */\nexport interface ItemSettings {\n\trequiredTools?: string[]; // PNP support IDs required for this item\n\trestrictedTools?: string[]; // PNP support IDs blocked for this item\n\ttoolParameters?: Record<string, any>; // Tool-specific config per item\n\n\t[key: string]: any; // Product extensions\n}\n\nexport interface SanctionedVersionEntity extends BaseEntity {\n\torganization?: string | OrganizationEntity; // if set, this is an organization specific matcher, otherwise, it's global\n\tmatch: string; // semver match expression, e.g. '1.*'\n\tpie: string; // e.g. @pie-element/calculator\n\tversion: string; // e.g. 2.14.24\n}\n\nexport enum SanctionedVersionChangeStatus {\n\tPENDING = \"pending\",\n\tIN_PROGRESS = \"in_progress\",\n\tCOMPLETED = \"completed\",\n\tFAILED = \"failed\",\n}\n\nexport interface SanctionedVersionChangeRequest {\n\tpie: string;\n\tmatch: string;\n\tversion: string;\n}\n\nexport interface SanctionedVersionChangeEntity extends BaseEntity {\n\torganization?: string | OrganizationEntity;\n\tcreatedBy: string | UserEntity;\n\tstatus: SanctionedVersionChangeStatus;\n\trequestedChanges: SanctionedVersionChangeRequest[];\n\tbeforeState: SanctionedVersionEntity[];\n\tsummary?: string;\n\tjobId: string;\n}\n\nexport type PlayerMode = \"gather\" | \"view\" | \"evaluate\" | \"author\";\n\nexport type PlayerRole = \"student\" | \"instructor\";\n\nexport interface Env {\n\tmode: PlayerMode;\n\trole: PlayerRole;\n\tpartialScoring?: boolean;\n}\n\nexport interface OutcomeResponse {\n\tid: string;\n\telement: string;\n\tscore?: number;\n\tempty?: boolean;\n\tcompleted?: boolean;\n\n\t[key: string]: any;\n}\n\nexport interface PieController {\n\tmodel(model: PieModel, sessionData: any[], env?: any): Promise<PieModel>;\n\n\toutcome(\n\t\tmodelOrSessionData: PieModel | any[],\n\t\tsessionOrEnv?: any,\n\t\tenv?: any,\n\t): Promise<OutcomeResponse>;\n\n\tscore: (config: object, session: object, env: object) => Promise<object>;\n\tcreateCorrectResponseSession: (\n\t\tconfig: object,\n\t\tenv: object,\n\t) => Promise<object>;\n\tvalidate: (model: object, config: object) => any;\n}\n\nexport interface PieItemElement {\n\t[elementName: string]: string;\n}\n\nexport type BundleInfo = {\n\turl: string;\n\thash: string;\n};\n\nexport type ItemSession = {\n\tid: string;\n\tdata: any[];\n};\n\nexport interface PieDefaultModel {\n\t// supports 'excess' properties as may be defined in pie models\n\t// https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#strict-object-literal-assignment-checking\n\t[x: string]: any;\n}\n\nexport interface PieContent {\n\tid: string;\n\t/**\n\t * Set of elements to include in the pie, provided in the format `{'element-name': 'mpm-package-name'}`\n\t */\n\telements: PieItemElement;\n\n\t/** Models for each PIE included in the item */\n\tmodels: PieModel[];\n\n\tmarkup: string;\n\n\tbundle?: BundleInfo;\n\n\tresources?: ConfigResource;\n\n\tdefaultExtraModels?: {\n\t\t[key: string]: PieDefaultModel;\n\t};\n}\n\nexport interface ConfigResource {\n\tstylesheets?: {\n\t\turl: string;\n\t}[];\n\tcontainerClass?: string;\n\tpassageContainerClass?: string;\n}\n\nexport interface AdvancedItemConfig {\n\tid: string;\n\tpie: PieContent;\n\tpassage?: PieContent;\n\tinstructorResources?: [PieContent];\n\tdefaultExtraModels?: {\n\t\t[key: string]: PieDefaultModel;\n\t};\n}\n\nexport type ItemConfig = PieContent | AdvancedItemConfig;\n\n/**\n * During the loading of elements from PIE bundles, we do a trick where we make\n * editor components available as web components by appending this to the element name.\n */\nexport const editorPostFix = \"-pie-editor--\";\n\nexport type ModelUpdatedDetail = {\n\tupdate: any;\n\treset: boolean;\n};\n\nexport class ModelUpdatedEvent extends CustomEvent<ModelUpdatedDetail> {\n\tstatic TYPE = \"model.updated\";\n\n\tconstructor(\n\t\treadonly update: any,\n\t\treadonly reset: boolean = false,\n\t) {\n\t\tsuper(ModelUpdatedEvent.TYPE, { bubbles: true, detail: { update, reset } });\n\t}\n}\n\nexport type DeleteDone = (e?: Error) => void;\n\nexport type DeleteImageDetail = {\n\tsrc: string;\n\tdone: DeleteDone;\n};\n\nexport class DeleteImageEvent extends CustomEvent<DeleteImageDetail> {\n\tstatic TYPE = \"delete.image\";\n\n\tconstructor(\n\t\treadonly src: string,\n\t\treadonly done: DeleteDone,\n\t) {\n\t\tsuper(DeleteImageEvent.TYPE, { bubbles: true, detail: { src, done } });\n\t}\n}\n\nexport interface ImageHandler {\n\tisPasted?: boolean;\n\tcancel: () => void;\n\tdone: (err?: Error, src?: string) => void;\n\tfileChosen: (file: File) => void;\n\tprogress: (percent: number, bytes: number, total: number) => void;\n}\n\nexport class InsertImageEvent extends CustomEvent<ImageHandler> {\n\tstatic TYPE = \"insert.image\";\n\n\tconstructor(readonly handler: ImageHandler) {\n\t\tsuper(InsertImageEvent.TYPE, { bubbles: true, detail: handler });\n\t}\n}\n\nexport type DeleteSoundDetail = {\n\tsrc: string;\n\tdone: DeleteDone;\n};\n\nexport class DeleteSoundEvent extends CustomEvent<DeleteSoundDetail> {\n\tstatic TYPE = \"delete.sound\";\n\n\tconstructor(\n\t\treadonly src: string,\n\t\treadonly done: DeleteDone,\n\t) {\n\t\tsuper(DeleteSoundEvent.TYPE, { bubbles: true, detail: { src, done } });\n\t}\n}\n\nexport interface SoundHandler {\n\tcancel: () => void;\n\tdone: (err?: Error, src?: string) => void;\n\tfileChosen: File;\n\tprogress: (percent: number, bytes: number, total: number) => void;\n}\n\nexport class InsertSoundEvent extends CustomEvent<SoundHandler> {\n\tstatic TYPE = \"insert.sound\";\n\n\tconstructor(readonly handler: SoundHandler) {\n\t\tsuper(InsertSoundEvent.TYPE, { bubbles: true, detail: handler });\n\t}\n}\n\nexport const isPassageEntity = (\n\tpassage: string | PassageEntity | undefined,\n): passage is PassageEntity => typeof passage === \"object\" && passage !== null;\n\nexport function isPrerelease(version: any): version is SemVer {\n\treturn (\n\t\ttypeof version === \"object\" &&\n\t\tversion !== null &&\n\t\ttypeof version.major === \"number\" &&\n\t\ttypeof version.minor === \"number\" &&\n\t\ttypeof version.patch === \"number\" &&\n\t\t!!version.prerelease && // Check if prerelease exists\n\t\ttypeof version.prerelease.tag === \"string\" &&\n\t\ttypeof version.prerelease.version === \"number\"\n\t);\n}\n\nexport function formatVersion(semVer: SemVer): string {\n\tif (!semVer) return \"\";\n\tconst base = [semVer.major, semVer.minor, semVer.patch].join(\".\");\n\tif (isPrerelease(semVer) && semVer.prerelease) {\n\t\tconst prerelease = [semVer.prerelease.tag, semVer.prerelease.version].join(\n\t\t\t\".\",\n\t\t);\n\t\treturn `${base}-${prerelease}`;\n\t}\n\treturn base;\n}\n\nexport interface PieElement extends HTMLElement {\n\tmodel: PieModel;\n\tconfiguration: any;\n\tsession: any[];\n}\n\nexport type SessionChangedDetail = {\n\tcomplete: boolean;\n\tcomponent: any;\n};\n\nexport class SessionChangedEvent extends CustomEvent<SessionChangedDetail> {\n\tstatic TYPE = \"session-changed\";\n\n\tconstructor(\n\t\treadonly component: string,\n\t\treadonly complete: boolean,\n\t) {\n\t\tsuper(SessionChangedEvent.TYPE, {\n\t\t\tbubbles: true,\n\t\t\tcomposed: true,\n\t\t\tdetail: { complete, component },\n\t\t} as any);\n\t}\n}\n\ninterface ItemCfg extends ConfigEntity {}\n\ninterface ItemWPassageCfg {\n\tpie: ItemCfg;\n\tpassage: ConfigEntity;\n}\n\nexport interface LoadResponse {\n\tjs: {\n\t\tview: string[];\n\t};\n\titem: ItemCfg | ItemWPassageCfg;\n\tsession: {\n\t\tid: string;\n\t\tdata: any[];\n\t};\n}\n\nexport interface Tracker {\n\ttrack(message: string, ...args: any[]): void;\n\n\tstart(label: string): void;\n\n\tend(label: string, metadata?: { [key: string]: any }): void;\n}\n\n/**\n * Interface for storing tracker messages with timestamps\n */\n/**\n * Interface for storing tracker messages with timestamps\n */\nexport interface TrackerMessage {\n\ttimestamp: Date;\n\tmessage: string;\n\targs: any[];\n\tformattedMessage: string; // Added formatted message with interpolated args\n}\n\n/**\n * Enhanced tracker that stores messages with timestamps\n */\nexport interface EnhancedTracker extends Tracker {\n\t/**\n\t * Get all raw tracker messages ordered by timestamp (ascending)\n\t */\n\tgetMessages(): TrackerMessage[];\n\n\t/**\n\t * Get a formatted multi-line representation of all tracker messages\n\t */\n\tgetFormattedMessages(): string;\n}\n\nexport interface ScoreResponse {\n\tsession: FlatSession;\n\tscore: SessionScore;\n\tempty?: boolean;\n\n\t[key: string]: any;\n}\n\nexport interface VersionDiff {\n\tversion: SemVer;\n\tpreviousVersion: SemVer | null;\n\tcreatedAt: Date;\n\tchanges: {\n\t\tadded: Record<string, any>;\n\t\tremoved: Record<string, any>;\n\t\tmodified: Record<\n\t\t\tstring,\n\t\t\t{\n\t\t\t\tprevious: any;\n\t\t\t\tcurrent: any;\n\t\t\t}\n\t\t>;\n\t};\n\tchangeCount: number;\n}\n\nexport interface RoleToOrganizationsMapping {\n\tid?: string;\n\trole: string;\n\torganizations: OrganizationEntity[] | string[];\n\tcreatedAt?: Date;\n\tupdatedAt?: Date;\n}\n\n// CMS Types (copied from @pie-api-aws/datastore to avoid importing server-only package)\nexport enum StandardType {\n\tROOT = 0,\n\tSUBJECT = 1,\n\tLEVEL = 2,\n\tSTANDARD = 3,\n}\n\nexport interface HierarchyInfo {\n\troot?: {\n\t\tid: string;\n\t\tguid: string;\n\t\ttitle: string;\n\t};\n\tsubject?: {\n\t\tid: string;\n\t\tguid: string;\n\t\ttitle: string;\n\t};\n\tlevel?: {\n\t\tid: string;\n\t\tguid: string;\n\t\ttitle: string;\n\t\tgrades: number[];\n\t};\n}\n\nexport interface CmsLearningStandardEntity extends BaseEntity {\n\tguid: string;\n\tabbr?: string;\n\ttype: StandardType;\n\tparentId?: string;\n\tparentGuid?: string;\n\tpath?: string;\n\tdepth: number;\n\tancestors: string[];\n\thierarchy?: HierarchyInfo;\n\ttitle?: string;\n\tdescription?: string;\n\tsequence?: string;\n\torderIndex?: number;\n\tsubjectArea?: string;\n\tsubjectName?: string;\n\tcategory?: string;\n\tabType?: string;\n\tabLabel?: string;\n\tsetName?: string;\n\tpublication?: string;\n\tadopted?: string;\n\tgrades: number[];\n\tgradeNames?: string[];\n\tproperties: Record<string, string>;\n\tstatus?: \"active\" | \"retired\" | \"deprecated\";\n\tlastModifiedAt?: Date;\n\treplacesGuid?: string;\n\treplacedByGuid?: string;\n\tdescriptionHtml?: string;\n\tlastSyncedAt?: Date;\n}\n\nexport interface ItemBankConfig {\n\tcollectionId: string;\n\tsubject?: string;\n\tgradeLevels?: number[];\n\torderIndex: number;\n}\n\nexport interface DOKDistribution {\n\tdok1?: number;\n\tdok2?: number;\n\tdok3?: number;\n\tdok4?: number;\n}\n\nexport interface AssessmentGoal {\n\tdokDistribution?: DOKDistribution;\n\tconstructedResponseCount?: number;\n\tpassageGoals?: string;\n\ttotalItemCount?: {\n\t\ttarget?: number;\n\t\tmin?: number;\n\t\tmax?: number;\n\t};\n}\n\nexport interface StudioImportMetadata {\n\tstudioAssessmentProgramId?: string;\n\tstudioPublicId?: string;\n\timportedAt?: Date;\n}\n\nexport interface CmsBlueprintEntity extends BaseEntity {\n\tname: string;\n\tdescription?: string;\n\tsubject?: string;\n\tstandardSet?: string;\n\tgradeLevels: number[];\n\tstates?: string[];\n\titemBankConfigs: ItemBankConfig[];\n\tassessmentGoal?: AssessmentGoal;\n\tclaimFramework?: \"SBAC\" | \"PARCC\" | \"STATE\" | \"CUSTOM\";\n\tclaimTargets?: string[];\n\tuseSBACClusterTarget?: boolean;\n\tclusters?: string[];\n\tstudioImportMetadata?: StudioImportMetadata;\n\toriginalPrompt?: string;\n}\n\nexport interface CmsBlueprintItemEntity extends BaseEntity {\n\tblueprintId: string;\n\tstandardGuid: string;\n\tcount: number;\n\tdetails?: string;\n\tassessmentId?: string;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"content-styles.js","sourceRoot":"","sources":["../../src/ui/content-styles.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Installs the shared PIE content stylesheet\n * (`@pie-players/pie-theme/components.css`) into the host document.\n *\n * Authored assessment content depends on classes that belong to no single\n * component: passage markup (`.numbered-paragraph`, `.p-number`,\n * `div.passage-title`), the legacy `kds-*` families, the `@media print`\n * `.noprint` rules, and the answer-eliminator classes. Players render into\n * light DOM, so those rules have to exist as a document-level stylesheet —\n * there is no shadow root to scope them to.\n *\n * Hosts used to be required to import that stylesheet themselves. Nothing\n * enforced it and nothing failed loudly when they didn't: the item rendered,\n * the passage was simply unstyled, and it surfaced as a visual bug reported by\n * hand days later. Players install the stylesheet themselves instead, which\n * keeps the host contract at \"import the player\".\n *\n * The CSS text is passed in rather than imported here: this package builds with\n * plain `tsc`, so it cannot inline a stylesheet. Bundler-built player packages\n * import it with Vite's `?inline` and hand the text over.\n */\n\n/** Marks a `<style>` element this module owns, and keeps installs idempotent. */\nconst MARKER_ATTRIBUTE = \"data-pie-content-styles\";\n\n/**\n * Declared by `components.css` itself, so it is observable no matter how the\n * stylesheet arrived — our injection or a host import. Used only to tell a host\n * that opted out but then shipped nothing.\n */\nconst SENTINEL_PROPERTY = \"--pie-content-styles\";\n\n/** `<html data-pie-content-styles=\"host\">` opts a host out of installation. */\nconst OPT_OUT_ATTRIBUTE = \"data-pie-content-styles\";\nconst OPT_OUT_VALUE = \"host\";\n\nexport type ContentStylesResult =\n\t| \"installed\"\n\t| \"already-installed\"\n\t| \"opted-out\"\n\t| \"no-document\";\n\nconst isBrowser = (): boolean =>\n\ttypeof document !== \"undefined\" && !!document.head;\n\n/**\n * True when the host declared `<html data-pie-content-styles=\"host\">`, i.e. it\n * takes ownership of loading the stylesheet.\n */\nexport function contentStylesOptedOut(): boolean {\n\tif (!isBrowser()) return false;\n\treturn (\n\t\tdocument.documentElement.getAttribute(OPT_OUT_ATTRIBUTE) === OPT_OUT_VALUE\n\t);\n}\n\n/**\n * True when `components.css` is applied to the document, by any route. Reads the\n * sentinel custom property the stylesheet declares on `:root`.\n *\n * Only meaningful once the document's stylesheets have been applied — a host\n * that loads CSS via an async `<link>` reads as missing until it lands.\n */\nexport function contentStylesPresent(): boolean {\n\tif (!isBrowser()) return false;\n\tconst value = getComputedStyle(document.documentElement)\n\t\t.getPropertyValue(SENTINEL_PROPERTY)\n\t\t.trim();\n\treturn value !== \"\";\n}\n\n/**\n * Installs `cssText` as a document-level stylesheet, once per document.\n *\n * The stylesheet is **prepended** to `<head>` and deliberately left out of a\n * cascade layer. Prepending reproduces the placement hosts were told to use by\n * hand — first in the entry, before app CSS — so an app rule and a content rule\n * of equal specificity resolve in the host's favour, exactly as before.\n *\n * A cascade layer looks tempting here and is the wrong tool: unlayered author\n * declarations beat *all* layered ones regardless of specificity, so a host\n * reset as broad as `p { margin: 0 }` would silently outrank\n * `.numbered-paragraph { margin-left: 36px }`. That trades a visible missing\n * stylesheet for a subtler override bug, so ordinary specificity wins instead.\n *\n * @param cssText Contents of `@pie-players/pie-theme/components.css`.\n * @param source Package installing the styles, for diagnostics.\n */\nexport function installContentStyles(\n\tcssText: string,\n\tsource: string,\n): ContentStylesResult {\n\tif (!isBrowser()) return \"no-document\";\n\tif (contentStylesOptedOut()) return \"opted-out\";\n\tif (document.querySelector(`style[${MARKER_ATTRIBUTE}]`)) {\n\t\t// Another player package, or a second copy of this one, already installed\n\t\t// the same stylesheet. Duplicating it would be harmless but pointless.\n\t\treturn \"already-installed\";\n\t}\n\tif (!cssText) return \"no-document\";\n\n\tconst style = document.createElement(\"style\");\n\tstyle.setAttribute(MARKER_ATTRIBUTE, source);\n\tstyle.textContent = cssText;\n\tdocument.head.prepend(style);\n\treturn \"installed\";\n}\n\n/**\n * Counts content stylesheets in the document that this module did not install —\n * i.e. copies the host loaded itself. Detected by the sentinel property rather\n * than by URL, so a copy arriving as a `<link>`, a bundler-injected `<style>`, or\n * anything else all count the same.\n *\n * Cross-origin sheets throw on `cssRules` access and are skipped; a host copy\n * served from another origin therefore reads as absent. That only costs a\n * diagnostic, never correctness.\n */\nconst countHostContentStyleSheets = (): number => {\n\t// Walks the owning elements rather than document.styleSheets: the marker\n\t// attribute lives on the element, and CSSStyleSheet.ownerNode is not\n\t// universally implemented (happy-dom omits it), which would make our own\n\t// installed copy look like a host copy.\n\tconst nodes = document.querySelectorAll<HTMLStyleElement | HTMLLinkElement>(\n\t\t`style:not([${MARKER_ATTRIBUTE}]), link[rel~=\"stylesheet\"]:not([${MARKER_ATTRIBUTE}])`,\n\t);\n\tlet count = 0;\n\tfor (const node of Array.from(nodes)) {\n\t\tlet rules: CSSRuleList | undefined;\n\t\ttry {\n\t\t\trules = node.sheet?.cssRules;\n\t\t} catch {\n\t\t\tcontinue;\n\t\t}\n\t\tif (!rules) continue;\n\t\tfor (const rule of Array.from(rules)) {\n\t\t\tif ((rule as CSSStyleRule).style?.getPropertyValue(SENTINEL_PROPERTY)) {\n\t\t\t\tcount += 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\treturn count;\n};\n\nlet auditWarningIssued = false;\nconst pendingChecks: ReturnType<typeof setTimeout>[] = [];\n\n/**\n * Reports, once per page, the two ways content styling can go wrong. Neither is\n * detectable synchronously — a host copy may still be in flight as an async\n * `<link>` — so the check is deferred.\n *\n * - **Missing**: the host opted out and then loaded nothing, so authored content\n * renders unstyled. This is the failure the old host-import contract produced\n * silently.\n * - **Duplicated**: the host still imports `components.css` itself *and* the\n * player installed a copy. Rendering is correct while the two agree, but the\n * host's copy loads later and therefore wins ties at equal specificity — so a\n * host copy pinned to an older `@pie-players/pie-theme` silently overrides the\n * player's newer rules. Harmless today, a confusing override tomorrow.\n */\nexport function auditContentStyles(source: string): void {\n\tif (!isBrowser() || auditWarningIssued) return;\n\n\tconst check = () => {\n\t\tif (auditWarningIssued) return;\n\n\t\tif (contentStylesOptedOut()) {\n\t\t\tif (contentStylesPresent()) return;\n\t\t\tauditWarningIssued = true;\n\t\t\tconsole.warn(\n\t\t\t\t`[${source}] No PIE content stylesheet found. This document sets ` +\n\t\t\t\t\t`${OPT_OUT_ATTRIBUTE}=\"${OPT_OUT_VALUE}\", so ${source} did not install ` +\n\t\t\t\t\t`one. Authored content that relies on shared classes ` +\n\t\t\t\t\t`(.numbered-paragraph, .p-number, div.passage-title, the kds-* ` +\n\t\t\t\t\t`families, answer-eliminator styles) will render unstyled. Either ` +\n\t\t\t\t\t`import \"@pie-players/pie-theme/components.css\" in the host app, or ` +\n\t\t\t\t\t`drop the ${OPT_OUT_ATTRIBUTE} attribute to let the player install it.`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (countHostContentStyleSheets() > 0) {\n\t\t\tauditWarningIssued = true;\n\t\t\tconsole.warn(\n\t\t\t\t`[${source}] The PIE content stylesheet is loaded twice: ${source} ` +\n\t\t\t\t\t`installs it, and this host also imports ` +\n\t\t\t\t\t`\"@pie-players/pie-theme/components.css\". Rendering is unaffected ` +\n\t\t\t\t\t`while both copies match, but the host copy loads later and wins ` +\n\t\t\t\t\t`ties at equal specificity, so a copy pinned to an older ` +\n\t\t\t\t\t`@pie-players/pie-theme will silently override newer player styles. ` +\n\t\t\t\t\t`Remove the host import, or set ` +\n\t\t\t\t\t`${OPT_OUT_ATTRIBUTE}=\"${OPT_OUT_VALUE}\" on <html> to own the ` +\n\t\t\t\t\t`stylesheet deliberately.`,\n\t\t\t);\n\t\t}\n\t};\n\n\t// A host stylesheet can land well after the module graph evaluates, and a\n\t// bundler-injected <link> is not necessarily parsed by the time `load` fires.\n\t// This is advisory output, so it is checked at a few widening points rather\n\t// than raced: whichever one first sees a settled document wins, and the latch\n\t// keeps the rest quiet. Missing the window costs a diagnostic, not\n\t// correctness.\n\tif (typeof requestAnimationFrame !== \"function\") {\n\t\tcheck();\n\t\treturn;\n\t}\n\trequestAnimationFrame(() => requestAnimationFrame(check));\n\tif (typeof window !== \"undefined\" && document.readyState !== \"complete\") {\n\t\twindow.addEventListener(\"load\", () => check(), { once: true });\n\t}\n\tif (typeof setTimeout === \"function\") {\n\t\tpendingChecks.push(setTimeout(check, 1000));\n\t}\n}\n\n/**\n * Test-only: clears the once-per-page warning latch and cancels any pending\n * deferred check, so a timer scheduled by one test cannot warn during the next.\n */\nexport function resetContentStylesWarningForTesting(): void {\n\tauditWarningIssued = false;\n\tfor (const handle of pendingChecks) clearTimeout(handle);\n\tpendingChecks.length = 0;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"debug-panel-persistence.js","sourceRoot":"","sources":["../../src/ui/debug-panel-persistence.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7E,MAAM,0BAA0B,GAAG,qBAAqB,CAAC;AAQzD,MAAM,UAAU,0BAA0B,CACzC,IAA8B;IAE9B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC,IAAI,EAAE,IAAI,SAAS,CAAC;IAClE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC;IAClE,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC;IAChE,OAAO,GAAG,0BAA0B,IAAI,KAAK,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAI,GAAW;IACjD,MAAM,GAAG,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAI,GAAW,EAAE,KAAQ;IAC5D,IAAI,CAAC;QACJ,mBAAmB,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACR,wCAAwC;IACzC,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC/C,IAAI,CAAC;QACJ,IAAI,OAAO,YAAY,KAAK,WAAW;YAAE,OAAO;QAChD,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACR,SAAS;IACV,CAAC;AACF,CAAC","sourcesContent":["import { safeLocalStorageGet, safeLocalStorageSet } from \"./safe-storage.js\";\n\nconst DEBUG_PANEL_STORAGE_PREFIX = \"pie:debug-panels:v1\";\n\ntype DebugPanelStorageKeyArgs = {\n\tscope: string;\n\tpanelId: string;\n\taspect?: string;\n};\n\nexport function createDebugPanelStorageKey(\n\targs: DebugPanelStorageKeyArgs,\n): string {\n\tconst scope = String(args.scope || \"default\").trim() || \"default\";\n\tconst panelId = String(args.panelId || \"panel\").trim() || \"panel\";\n\tconst aspect = String(args.aspect || \"state\").trim() || \"state\";\n\treturn `${DEBUG_PANEL_STORAGE_PREFIX}:${scope}:${panelId}:${aspect}`;\n}\n\nexport function readDebugPanelState<T>(key: string): T | null {\n\tconst raw = safeLocalStorageGet(key);\n\tif (!raw) return null;\n\ttry {\n\t\treturn JSON.parse(raw) as T;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function writeDebugPanelState<T>(key: string, value: T): void {\n\ttry {\n\t\tsafeLocalStorageSet(key, JSON.stringify(value));\n\t} catch {\n\t\t// ignore serialization/storage failures\n\t}\n}\n\nexport function clearDebugPanelState(key: string): void {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return;\n\t\tlocalStorage.removeItem(key);\n\t} catch {\n\t\t// ignore\n\t}\n}\n\nexport type DebugPanelLayoutStateV1 = {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n\tminimized: boolean;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"first-focusable.js","sourceRoot":"","sources":["../../src/ui/first-focusable.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IACjC,SAAS;IACT,wBAAwB;IACxB,4CAA4C;IAC5C,wBAAwB;IACxB,0BAA0B;IAC1B,kDAAkD;IAClD,iCAAiC;CACjC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,EAAe;IACxD,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,kBAAkB,CAAC;QAAE,OAAO,KAAK,CAAC;IAClD,IAAI,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9C,IAAI,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,OAAO,EAAE,CAAC,YAAY,KAAK,IAAI,IAAI,EAAE,CAAC,cAAc,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;AACnE,CAAC","sourcesContent":["/**\n * Selector for elements that are commonly keyboard-focusable in assessment UI.\n * Kept in sync with `focus-trap.ts` for consistent tab-order surfaces.\n */\nexport const FOCUSABLE_SELECTOR = [\n\t\"a[href]\",\n\t\"button:not([disabled])\",\n\t\"input:not([disabled]):not([type='hidden'])\",\n\t\"select:not([disabled])\",\n\t\"textarea:not([disabled])\",\n\t\"[contenteditable]:not([contenteditable='false'])\",\n\t\"[tabindex]:not([tabindex='-1'])\",\n].join(\",\");\n\n/**\n * Whether `el` is a reasonable programmatic focus target (matches selector,\n * not disabled, not inside `inert`, and has a layout box).\n */\nexport function isProgrammaticFocusTarget(el: HTMLElement): boolean {\n\tif (!el.matches(FOCUSABLE_SELECTOR)) return false;\n\tif (el.hasAttribute(\"disabled\")) return false;\n\tif (el.closest(\"[inert]\")) return false;\n\treturn el.offsetParent !== null || el.getClientRects().length > 0;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"focus-trap.js","sourceRoot":"","sources":["../../src/ui/focus-trap.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tFOCUSABLE_SELECTOR,\n\tisProgrammaticFocusTarget,\n} from \"./first-focusable.js\";\n\ntype FocusTrapOptions = {\n\tinitialFocus?: HTMLElement | null;\n\tonEscape?: () => void;\n\t/**\n\t * When true (default), Tab from the last focusable wraps to the first and\n\t * Shift+Tab from the first wraps to the last — appropriate for modal dialogs\n\t * and popovers. When false, tab boundaries fall through to the browser's\n\t * natural tab order, so users can step out of the container in either\n\t * direction. Escape handling is unaffected.\n\t */\n\twrap?: boolean;\n\t/**\n\t * Fires when Tab attempts to leave the trap from a boundary. Only invoked\n\t * when `wrap` is false. The handler may call `event.preventDefault()` and\n\t * move focus to a custom destination; otherwise the browser's natural tab\n\t * order applies.\n\t */\n\tonTabExit?: (direction: \"forward\" | \"backward\", event: KeyboardEvent) => void;\n};\n\nfunction getFocusableElements(container: HTMLElement): HTMLElement[] {\n\treturn Array.from(\n\t\tcontainer.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR),\n\t).filter((el) => isProgrammaticFocusTarget(el));\n}\n\nfunction focusInitialTarget(\n\tcontainer: HTMLElement,\n\tinitialFocus?: HTMLElement | null,\n): void {\n\ttry {\n\t\tif (initialFocus && container.contains(initialFocus)) {\n\t\t\tinitialFocus.focus();\n\t\t\treturn;\n\t\t}\n\t\tconst focusable = getFocusableElements(container);\n\t\tif (focusable.length > 0) {\n\t\t\tfocusable[0].focus();\n\t\t\treturn;\n\t\t}\n\t\tcontainer.focus?.();\n\t} catch {\n\t\t// ignore\n\t}\n}\n\n/**\n * Focus trap helper for floating dialogs/panels.\n *\n * Keeps tab navigation contained, supports Escape callback,\n * and restores prior focus when the trap is removed.\n */\nexport function createFocusTrap(\n\tcontainer: HTMLElement,\n\toptions: FocusTrapOptions = {},\n): () => void {\n\tconst prev =\n\t\ttypeof document !== \"undefined\"\n\t\t\t? (document.activeElement as HTMLElement | null)\n\t\t\t: null;\n\tconst wrap = options.wrap ?? true;\n\tconst onKeydown = (event: KeyboardEvent) => {\n\t\tif (event.key === \"Escape\") {\n\t\t\toptions.onEscape?.();\n\t\t\treturn;\n\t\t}\n\t\tif (event.key !== \"Tab\") return;\n\n\t\tconst focusable = getFocusableElements(container);\n\t\tif (!focusable.length) {\n\t\t\tif (!wrap) return;\n\t\t\tevent.preventDefault();\n\t\t\tcontainer.focus?.();\n\t\t\treturn;\n\t\t}\n\n\t\tconst current = document.activeElement as HTMLElement | null;\n\t\tconst currentIndex = focusable.indexOf(current || focusable[0]);\n\t\tif (event.shiftKey) {\n\t\t\tif (currentIndex <= 0) {\n\t\t\t\tif (wrap) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tfocusable[focusable.length - 1].focus();\n\t\t\t\t} else {\n\t\t\t\t\toptions.onTabExit?.(\"backward\", event);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (currentIndex === focusable.length - 1) {\n\t\t\tif (wrap) {\n\t\t\t\tevent.preventDefault();\n\t\t\t\tfocusable[0].focus();\n\t\t\t} else {\n\t\t\t\toptions.onTabExit?.(\"forward\", event);\n\t\t\t}\n\t\t}\n\t};\n\n\tqueueMicrotask(() => focusInitialTarget(container, options.initialFocus));\n\tcontainer.addEventListener(\"keydown\", onKeydown);\n\n\treturn () => {\n\t\tcontainer.removeEventListener(\"keydown\", onKeydown);\n\t\ttry {\n\t\t\tprev?.focus?.();\n\t\t} catch {\n\t\t\t// ignore\n\t\t}\n\t};\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"safe-storage.js","sourceRoot":"","sources":["../../src/ui/safe-storage.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB,CAAC,GAAW;IAC9C,IAAI,CAAC;QACJ,IAAI,OAAO,YAAY,KAAK,WAAW;YAAE,OAAO,IAAI,CAAC;QACrD,OAAO,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,GAAW,EAAE,KAAa;IAC7D,IAAI,CAAC;QACJ,IAAI,OAAO,YAAY,KAAK,WAAW;YAAE,OAAO;QAChD,YAAY,CAAC,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACR,SAAS;IACV,CAAC;AACF,CAAC","sourcesContent":["export function safeLocalStorageGet(key: string): string | null {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return null;\n\t\treturn localStorage.getItem(key);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function safeLocalStorageSet(key: string, value: string): void {\n\ttry {\n\t\tif (typeof localStorage === \"undefined\") return;\n\t\tlocalStorage.setItem(key, value);\n\t} catch {\n\t\t// ignore\n\t}\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"zoom-compensation.js","sourceRoot":"","sources":["../../src/ui/zoom-compensation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAYH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAA4B;IAChE,OAAO,EAAE,CAAC;IACV,eAAe,EAAE,IAAI;CACrB,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACxC,UAAkB,EAClB,UAAkB;IAElB,MAAM,KAAK,GAAG,UAAU,GAAG,UAAU,CAAC;IACtC,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,uBAAuB,CACtC,IAAY,EACZ,OAAe,EACf,eAAuB;IAEvB,OAAO,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC;AAC/D,CAAC","sourcesContent":["/**\n * Pure browser-zoom compensation math (framework-agnostic, no Svelte runes).\n *\n * Browser zoom is approximated as outerWidth / innerWidth: outerWidth is the\n * OS window size (zoom-independent) while innerWidth is in CSS pixels (shrinks\n * as zoom increases). Callers apply the returned factor via CSS `zoom` so an\n * element that would otherwise keep enlarging past `maxZoom` compounds back\n * down to the cap.\n *\n * This module is tsc-buildable and shipped in `dist`, so it can be consumed by\n * packages that resolve `@pie-players/pie-players-shared` from its published\n * build (e.g. the assessment-toolkit CE bundle) as well as by the reactive\n * Svelte wrapper in `./use-zoom-compensation.svelte.ts`, which packages built\n * with Vite alias to source.\n */\n\nexport type ZoomCompensationOptions = {\n\t/**\n\t * Zoom level (as a ratio, e.g. `2` for 200%) below which the factor is 1.\n\t * Above this level the factor shrinks as `maxZoom / zoom`.\n\t */\n\tmaxZoom: number;\n\t/** Lower bound on the returned factor. */\n\tminCompensation: number;\n};\n\n/**\n * Shared zoom-cap settings for the toolbar icon buttons (the TTS play button and\n * the calculator button) so both compensate identically and can't drift apart.\n * Grow normally up to 200%, then freeze; the 0.25 floor keeps the cap holding\n * past 500% browser zoom (see the minCompensation note in\n * section-player's SectionPlayerTabbedContent).\n */\nexport const ICON_BUTTON_ZOOM_OPTIONS: ZoomCompensationOptions = {\n\tmaxZoom: 2,\n\tminCompensation: 0.25,\n};\n\n/**\n * Approximate the browser zoom level from an outer/inner width pair. Falls\n * back to 1 (100%) if the ratio isn't finite or is non-positive (which happens\n * during SSR, in headless envs, and briefly during resize on some browsers).\n */\nexport function approximateZoomFromWidths(\n\touterWidth: number,\n\tinnerWidth: number,\n): number {\n\tconst ratio = outerWidth / innerWidth;\n\treturn Number.isFinite(ratio) && ratio > 0 ? ratio : 1;\n}\n\n/**\n * Pure zoom-compensation math: exactly 1 at zoom <= maxZoom, then shrinks as\n * `maxZoom / zoom`, floored at `minCompensation` to guard against inflated\n * ratios from window chrome / side panels making the element unusably small.\n */\nexport function computeZoomCompensation(\n\tzoom: number,\n\tmaxZoom: number,\n\tminCompensation: number,\n): number {\n\treturn Math.max(minCompensation, Math.min(1, maxZoom / zoom));\n}\n"]}