@pie-players/pie-assessment-toolkit 0.3.44 → 0.3.46

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 (191) hide show
  1. package/README.md +59 -70
  2. package/dist/components/ItemToolBar.custom-element.js +776 -357
  3. package/dist/components/PieAssessmentToolkit.custom-element.js +19501 -3591
  4. package/dist/components/SectionToolBar.custom-element.js +1058 -540
  5. package/dist/index.d.ts +3 -6
  6. package/dist/index.js +1 -3
  7. package/dist/index.js.map +1 -1
  8. package/dist/policy/core/ToolPolicyEngine.d.ts +1 -2
  9. package/dist/policy/core/ToolPolicyEngine.js +1 -2
  10. package/dist/policy/core/ToolPolicyEngine.js.map +1 -1
  11. package/dist/policy/core/pnp-policy-inputs.d.ts +2 -3
  12. package/dist/policy/core/pnp-policy-inputs.js +2 -3
  13. package/dist/policy/core/pnp-policy-inputs.js.map +1 -1
  14. package/dist/policy/core/provenance.d.ts +7 -34
  15. package/dist/policy/core/provenance.js +4 -21
  16. package/dist/policy/core/provenance.js.map +1 -1
  17. package/dist/policy/sources/PnpPolicySource.d.ts +6 -19
  18. package/dist/policy/sources/PnpPolicySource.js +6 -19
  19. package/dist/policy/sources/PnpPolicySource.js.map +1 -1
  20. package/dist/runtime/SectionRuntimeEngine.d.ts +13 -28
  21. package/dist/runtime/SectionRuntimeEngine.js +13 -27
  22. package/dist/runtime/SectionRuntimeEngine.js.map +1 -1
  23. package/dist/runtime/adapter/SectionEngineAdapter.d.ts +2 -2
  24. package/dist/runtime/adapter/SectionEngineAdapter.js +2 -2
  25. package/dist/runtime/adapter/SectionEngineAdapter.js.map +1 -1
  26. package/dist/runtime/adapter/coordinator-bridge.d.ts +1 -1
  27. package/dist/runtime/adapter/coordinator-bridge.js +1 -1
  28. package/dist/runtime/adapter/coordinator-bridge.js.map +1 -1
  29. package/dist/runtime/adapter/dom-event-bridge.d.ts +1 -1
  30. package/dist/runtime/adapter/dom-event-bridge.js +1 -1
  31. package/dist/runtime/adapter/dom-event-bridge.js.map +1 -1
  32. package/dist/runtime/catalog-registration.d.ts +12 -0
  33. package/dist/runtime/catalog-registration.js +54 -0
  34. package/dist/runtime/catalog-registration.js.map +1 -0
  35. package/dist/runtime/core/engine-output.d.ts +2 -2
  36. package/dist/runtime/core/engine-output.js +2 -2
  37. package/dist/runtime/core/engine-output.js.map +1 -1
  38. package/dist/runtime/core/engine-readiness.d.ts +2 -2
  39. package/dist/runtime/core/engine-readiness.js.map +1 -1
  40. package/dist/runtime/core/engine-resolver.d.ts +13 -41
  41. package/dist/runtime/core/engine-resolver.js +19 -76
  42. package/dist/runtime/core/engine-resolver.js.map +1 -1
  43. package/dist/runtime/core/engine-transition.d.ts +1 -1
  44. package/dist/runtime/core/engine-transition.js +1 -1
  45. package/dist/runtime/core/engine-transition.js.map +1 -1
  46. package/dist/runtime/engine.d.ts +1 -1
  47. package/dist/runtime/engine.js.map +1 -1
  48. package/dist/runtime/section-runtime-engine-host-context.d.ts +22 -15
  49. package/dist/runtime/section-runtime-engine-host-context.js +10 -10
  50. package/dist/runtime/section-runtime-engine-host-context.js.map +1 -1
  51. package/dist/runtime/stage-emit-gate.d.ts +4 -4
  52. package/dist/runtime/stage-emit-gate.js +3 -3
  53. package/dist/runtime/stage-emit-gate.js.map +1 -1
  54. package/dist/services/AccessibilityCatalogResolver.d.ts +23 -0
  55. package/dist/services/AccessibilityCatalogResolver.js +200 -28
  56. package/dist/services/AccessibilityCatalogResolver.js.map +1 -1
  57. package/dist/services/HighlightCoordinator.d.ts +25 -0
  58. package/dist/services/HighlightCoordinator.js +125 -0
  59. package/dist/services/HighlightCoordinator.js.map +1 -1
  60. package/dist/services/SSMLExtractor.d.ts +16 -1
  61. package/dist/services/SSMLExtractor.js +86 -7
  62. package/dist/services/SSMLExtractor.js.map +1 -1
  63. package/dist/services/TTSService.d.ts +30 -4
  64. package/dist/services/TTSService.js +652 -159
  65. package/dist/services/TTSService.js.map +1 -1
  66. package/dist/services/ToolRegistry.d.ts +11 -2
  67. package/dist/services/ToolRegistry.js +1 -1
  68. package/dist/services/ToolRegistry.js.map +1 -1
  69. package/dist/services/ToolkitCoordinator.d.ts +16 -73
  70. package/dist/services/ToolkitCoordinator.js +34 -113
  71. package/dist/services/ToolkitCoordinator.js.map +1 -1
  72. package/dist/services/interfaces.d.ts +20 -2
  73. package/dist/services/interfaces.js.map +1 -1
  74. package/dist/services/tool-config-validation.d.ts +1 -1
  75. package/dist/services/tool-config-validation.js +7 -7
  76. package/dist/services/tool-config-validation.js.map +1 -1
  77. package/dist/services/tool-instance-id.js +1 -1
  78. package/dist/services/tool-instance-id.js.map +1 -1
  79. package/dist/services/tool-providers/TTSToolProvider.js +3 -3
  80. package/dist/services/tool-providers/TTSToolProvider.js.map +1 -1
  81. package/dist/services/tools-config-normalizer.d.ts +13 -2
  82. package/dist/services/tools-config-normalizer.js +3 -8
  83. package/dist/services/tools-config-normalizer.js.map +1 -1
  84. package/dist/services/tts/browser-provider.js +2 -0
  85. package/dist/services/tts/browser-provider.js.map +1 -1
  86. package/dist/services/tts/catalog-span-alignment.d.ts +39 -0
  87. package/dist/services/tts/catalog-span-alignment.js +300 -0
  88. package/dist/services/tts/catalog-span-alignment.js.map +1 -0
  89. package/dist/services/tts/generated-speech/assemble-plan.d.ts +33 -0
  90. package/dist/services/tts/generated-speech/assemble-plan.js +111 -0
  91. package/dist/services/tts/generated-speech/assemble-plan.js.map +1 -0
  92. package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +25 -0
  93. package/dist/services/tts/generated-speech/dom/build-dom-plan.js +66 -0
  94. package/dist/services/tts/generated-speech/dom/build-dom-plan.js.map +1 -0
  95. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.d.ts +19 -0
  96. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js +71 -0
  97. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js.map +1 -0
  98. package/dist/services/tts/generated-speech/dom/types.d.ts +37 -0
  99. package/dist/services/tts/generated-speech/dom/types.js +2 -0
  100. package/dist/services/tts/generated-speech/dom/types.js.map +1 -0
  101. package/dist/services/tts/generated-speech/index.d.ts +17 -0
  102. package/dist/services/tts/generated-speech/index.js +17 -0
  103. package/dist/services/tts/generated-speech/index.js.map +1 -0
  104. package/dist/services/tts/generated-speech/math-speech-cache.d.ts +15 -0
  105. package/dist/services/tts/generated-speech/math-speech-cache.js +78 -0
  106. package/dist/services/tts/generated-speech/math-speech-cache.js.map +1 -0
  107. package/dist/services/tts/generated-speech/types.d.ts +104 -0
  108. package/dist/services/tts/generated-speech/types.js +18 -0
  109. package/dist/services/tts/generated-speech/types.js.map +1 -0
  110. package/dist/services/tts/highlight-pipeline/boundary-normalizer.d.ts +2 -0
  111. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +106 -0
  112. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js.map +1 -0
  113. package/dist/services/tts/highlight-pipeline/create-highlight-plan.d.ts +6 -0
  114. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +217 -0
  115. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js.map +1 -0
  116. package/dist/services/tts/highlight-pipeline/highlight-policy.d.ts +2 -0
  117. package/dist/services/tts/highlight-pipeline/highlight-policy.js +32 -0
  118. package/dist/services/tts/highlight-pipeline/highlight-policy.js.map +1 -0
  119. package/dist/services/tts/highlight-pipeline/index.d.ts +9 -0
  120. package/dist/services/tts/highlight-pipeline/index.js +9 -0
  121. package/dist/services/tts/highlight-pipeline/index.js.map +1 -0
  122. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.d.ts +3 -0
  123. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js +32 -0
  124. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js.map +1 -0
  125. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.d.ts +29 -0
  126. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +44 -0
  127. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js.map +1 -0
  128. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.d.ts +2 -0
  129. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +20 -0
  130. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js.map +1 -0
  131. package/dist/services/tts/highlight-pipeline/readable-region.d.ts +1 -0
  132. package/dist/services/tts/highlight-pipeline/readable-region.js +19 -0
  133. package/dist/services/tts/highlight-pipeline/readable-region.js.map +1 -0
  134. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.d.ts +6 -0
  135. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js +198 -0
  136. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js.map +1 -0
  137. package/dist/services/tts/highlight-pipeline/types.d.ts +72 -0
  138. package/dist/services/tts/highlight-pipeline/types.js +2 -0
  139. package/dist/services/tts/highlight-pipeline/types.js.map +1 -0
  140. package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +2 -0
  141. package/dist/services/tts/highlight-pipeline/visible-map-range.js +22 -0
  142. package/dist/services/tts/highlight-pipeline/visible-map-range.js.map +1 -0
  143. package/dist/services/tts/math-alignment/index.d.ts +30 -0
  144. package/dist/services/tts/math-alignment/index.js +66 -0
  145. package/dist/services/tts/math-alignment/index.js.map +1 -0
  146. package/dist/services/tts/math-alignment/mathml-tokenizer.d.ts +2 -0
  147. package/dist/services/tts/math-alignment/mathml-tokenizer.js +317 -0
  148. package/dist/services/tts/math-alignment/mathml-tokenizer.js.map +1 -0
  149. package/dist/services/tts/math-alignment/range-resolver.d.ts +10 -0
  150. package/dist/services/tts/math-alignment/range-resolver.js +13 -0
  151. package/dist/services/tts/math-alignment/range-resolver.js.map +1 -0
  152. package/dist/services/tts/math-alignment/sequence-aligner.d.ts +7 -0
  153. package/dist/services/tts/math-alignment/sequence-aligner.js +119 -0
  154. package/dist/services/tts/math-alignment/sequence-aligner.js.map +1 -0
  155. package/dist/services/tts/math-alignment/speech-tokenizer.d.ts +31 -0
  156. package/dist/services/tts/math-alignment/speech-tokenizer.js +143 -0
  157. package/dist/services/tts/math-alignment/speech-tokenizer.js.map +1 -0
  158. package/dist/services/tts/math-alignment/types.d.ts +58 -0
  159. package/dist/services/tts/math-alignment/types.js +2 -0
  160. package/dist/services/tts/math-alignment/types.js.map +1 -0
  161. package/dist/services/tts/math-aware-text-processing.d.ts +18 -0
  162. package/dist/services/tts/math-aware-text-processing.js +348 -0
  163. package/dist/services/tts/math-aware-text-processing.js.map +1 -0
  164. package/dist/services/tts/math-speech.d.ts +44 -0
  165. package/dist/services/tts/math-speech.js +147 -0
  166. package/dist/services/tts/math-speech.js.map +1 -0
  167. package/dist/services/tts/mathml-sanitization.d.ts +1 -0
  168. package/dist/services/tts/mathml-sanitization.js +170 -0
  169. package/dist/services/tts/mathml-sanitization.js.map +1 -0
  170. package/dist/services/tts/ssml/spoken-text.d.ts +33 -0
  171. package/dist/services/tts/ssml/spoken-text.js +159 -0
  172. package/dist/services/tts/ssml/spoken-text.js.map +1 -0
  173. package/dist/services/tts/text-processing.d.ts +3 -0
  174. package/dist/services/tts/text-processing.js +30 -4
  175. package/dist/services/tts/text-processing.js.map +1 -1
  176. package/dist/services/tts-runtime-config.d.ts +17 -1
  177. package/dist/services/tts-runtime-config.js +11 -1
  178. package/dist/services/tts-runtime-config.js.map +1 -1
  179. package/dist/tools/registrations/accessibility-tools.js +0 -1
  180. package/dist/tools/registrations/accessibility-tools.js.map +1 -1
  181. package/dist/tools/registrations/calculator.js +46 -5
  182. package/dist/tools/registrations/calculator.js.map +1 -1
  183. package/dist/tools/tool-tag-map.js +0 -1
  184. package/dist/tools/tool-tag-map.js.map +1 -1
  185. package/package.json +8 -11
  186. package/dist/services/PNPToolResolver.d.ts +0 -280
  187. package/dist/services/PNPToolResolver.js +0 -514
  188. package/dist/services/PNPToolResolver.js.map +0 -1
  189. package/dist/services/pnp-provenance.d.ts +0 -166
  190. package/dist/services/pnp-provenance.js +0 -231
  191. package/dist/services/pnp-provenance.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"PnpPolicySource.js","sourceRoot":"","sources":["../../../src/policy/sources/PnpPolicySource.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * PNP Policy Source — refactor of `PnpToolResolver` for the M8 engine\n * (see `.cursor/plans/m8-design.md` § 3 step 5 and § 5 §).\n *\n * This source applies the PNP/profile precedence rules used by today's\n * `PnpToolResolver` but exposes them as a `(candidates, pnpPolicyInputs) →\n * (refinedCandidates, perToolFlags, mandates, decisions)` function the\n * engine can call once per `decide(...)` request, with all results\n * routed through the unified `ToolPolicyProvenanceBuilder`.\n *\n * **Why this lives next to the legacy resolver instead of replacing\n * it.** PR 1 introduces the engine without callers; the legacy\n * `PnpToolResolver` is still in active use by `<pie-item-toolbar>`\n * and by external tests. PR 5 deletes the legacy class once the\n * toolbar (PR 3) and the PNP default flip (PR 4) have landed and the\n * caller list is empty. Until then this source is the *canonical*\n * implementation and the legacy class is the deprecated mirror.\n */\n\nimport type {\n\tAssessmentEntity,\n\tAssessmentItemRef,\n\tAssessmentSettings,\n\tItemSettings,\n\tPersonalNeedsProfile,\n} from \"@pie-players/pie-players-shared/types\";\n\nimport type { ToolRegistry } from \"../../services/ToolRegistry.js\";\nimport type {\n\tToolPolicyResolutionDecision,\n\tToolPolicySourceType,\n} from \"../core/provenance.js\";\nimport type { PnpPolicySourceRule } from \"../core/policy-source-tag.js\";\n\n/** Per-tool flags PNP/profile policy may attach to a surviving entry. */\nexport interface PnpPolicyToolFlags {\n\t/** PNP/profile policy mandates this tool (item or district `requiredTools`). */\n\trequired: boolean;\n\t/** Student PNP marks this tool as a support (host UI cannot toggle off). */\n\talwaysAvailable: boolean;\n\t/** Tool-specific settings derived from item / assessment settings. */\n\tsettings?: unknown;\n\t/** Which PNP/profile rule contributed the surviving verdict. */\n\trule: PnpPolicySourceRule;\n\t/** Source attribution for `ToolPolicyEntry.sources`. */\n\tsourceType: ToolPolicySourceType;\n}\n\nexport interface PnpPolicyApplyArgs {\n\tassessment?: AssessmentEntity | null;\n\tcurrentItemRef?: AssessmentItemRef;\n}\n\n/**\n * Provenance event the engine should append to its builder. Pre-baked\n * here so the source has a single allocation pattern and the engine\n * keeps a uniform `addDecision(...)` shape.\n */\nexport interface PnpPolicyDecisionEvent {\n\tprecedence: 1 | 2 | 3 | 4 | 5 | 6;\n\trule:\n\t\t| \"district-block\"\n\t\t| \"test-admin-override\"\n\t\t| \"item-restriction\"\n\t\t| \"item-requirement\"\n\t\t| \"district-requirement\"\n\t\t| \"pnp-support\"\n\t\t| \"pnp-prohibited\";\n\tfeatureId: string;\n\taction: ToolPolicyResolutionDecision[\"action\"];\n\tsourceType: ToolPolicySourceType;\n\treason: string;\n\tvalue?: unknown;\n}\n\nexport interface PnpPolicyResult {\n\t/**\n\t * Tool IDs PNP/profile policy explicitly blocked. Engine removes these from the\n\t * candidate set in step 5.\n\t */\n\tblockedToolIds: Set<string>;\n\t/**\n\t * Tool IDs PNP/profile policy mandates (item or district `requiredTools`). Used\n\t * by the engine to detect `tool-policy.requiredToolBlocked`\n\t * diagnostics for tools removed by host policy.\n\t */\n\tmandatedToolIds: Set<string>;\n\t/**\n\t * Per-tool flags merged into surviving `ToolPolicyEntry`s.\n\t */\n\tperToolFlags: Map<string, PnpPolicyToolFlags>;\n\t/**\n\t * Decision log entries the engine must record. Order matches the\n\t * order rules fired internally (highest precedence first per\n\t * support id).\n\t */\n\tdecisions: PnpPolicyDecisionEvent[];\n\t/** Configuration sources the engine should attach to its provenance. */\n\tsources: {\n\t\tassessment?: { id: string; name: string; config?: unknown };\n\t\tstudent?: { id: string; name: string; config?: unknown };\n\t\titem?: { id: string; name: string; config?: unknown };\n\t};\n}\n\n/**\n * Internal context: every PNP/profile policy input bundled together for the rule\n * evaluation loop.\n */\ninterface PnpResolutionContext {\n\tpnp?: PersonalNeedsProfile;\n\tdistrictPolicy?: AssessmentSettings[\"districtPolicy\"];\n\ttestAdmin?: AssessmentSettings[\"testAdministration\"];\n\titemSettings?: ItemSettings;\n\ttoolConfigs?: AssessmentSettings[\"toolConfigs\"];\n}\n\nexport class PnpPolicySource {\n\treadonly id = \"pnp\";\n\tprivate readonly toolRegistry: ToolRegistry;\n\n\tconstructor(toolRegistry: ToolRegistry) {\n\t\tthis.toolRegistry = toolRegistry;\n\t}\n\n\tapply(args: PnpPolicyApplyArgs): PnpPolicyResult {\n\t\tconst { assessment, currentItemRef } = args;\n\t\tconst pnp = assessment?.personalNeedsProfile;\n\t\tconst settings = assessment?.settings as AssessmentSettings | undefined;\n\t\tconst itemSettings = currentItemRef?.settings as ItemSettings | undefined;\n\n\t\tconst result: PnpPolicyResult = {\n\t\t\tblockedToolIds: new Set(),\n\t\t\tmandatedToolIds: new Set(),\n\t\t\tperToolFlags: new Map(),\n\t\t\tdecisions: [],\n\t\t\tsources: {},\n\t\t};\n\n\t\tif (settings?.districtPolicy || settings?.testAdministration) {\n\t\t\tresult.sources.assessment = {\n\t\t\t\tid: assessment?.id || \"unknown\",\n\t\t\t\tname: assessment?.name || assessment?.id || \"Assessment\",\n\t\t\t\tconfig: settings,\n\t\t\t};\n\t\t}\n\t\tif (pnp) {\n\t\t\tresult.sources.student = {\n\t\t\t\tid: \"student\",\n\t\t\t\tname: \"Student PNP Profile\",\n\t\t\t\tconfig: pnp,\n\t\t\t};\n\t\t}\n\t\tif (currentItemRef && itemSettings) {\n\t\t\tresult.sources.item = {\n\t\t\t\tid: currentItemRef.identifier,\n\t\t\t\tname: currentItemRef.identifier,\n\t\t\t\tconfig: itemSettings,\n\t\t\t};\n\t\t}\n\n\t\tconst allSupports = new Set<string>();\n\t\tpnp?.supports?.forEach((s) => allSupports.add(s));\n\t\tpnp?.prohibitedSupports?.forEach((s) => allSupports.add(s));\n\t\tsettings?.districtPolicy?.blockedTools?.forEach((s) => allSupports.add(s));\n\t\tsettings?.districtPolicy?.requiredTools?.forEach((s) => allSupports.add(s));\n\t\titemSettings?.requiredTools?.forEach((s) => allSupports.add(s));\n\t\titemSettings?.restrictedTools?.forEach((s) => allSupports.add(s));\n\n\t\tconst ctx: PnpResolutionContext = {\n\t\t\tpnp,\n\t\t\tdistrictPolicy: settings?.districtPolicy,\n\t\t\ttestAdmin: settings?.testAdministration,\n\t\t\titemSettings,\n\t\t\ttoolConfigs: settings?.toolConfigs,\n\t\t};\n\n\t\tfor (const supportId of allSupports) {\n\t\t\tthis.resolveSupport(supportId, ctx, result);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate resolveSupport(\n\t\tsupportId: string,\n\t\tctx: PnpResolutionContext,\n\t\tout: PnpPolicyResult,\n\t): void {\n\t\t// 1. District block (absolute veto)\n\t\tif (ctx.districtPolicy?.blockedTools?.includes(supportId)) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.blockedToolIds.add(toolId);\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 1,\n\t\t\t\trule: \"district-block\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"block\",\n\t\t\t\tsourceType: \"assessment\",\n\t\t\t\treason: `District policy blocks \"${supportId}\" for all assessments`,\n\t\t\t\tvalue: ctx.districtPolicy.blockedTools,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// 2. Test administration override\n\t\tif (ctx.testAdmin?.toolOverrides?.[supportId] === false) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.blockedToolIds.add(toolId);\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 2,\n\t\t\t\trule: \"test-admin-override\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"block\",\n\t\t\t\tsourceType: \"assessment\",\n\t\t\t\treason: `Test administrator disabled \"${supportId}\" for this session`,\n\t\t\t\tvalue: ctx.testAdmin.toolOverrides,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// 3. Item restriction (per-item block)\n\t\tif (ctx.itemSettings?.restrictedTools?.includes(supportId)) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.blockedToolIds.add(toolId);\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 3,\n\t\t\t\trule: \"item-restriction\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"block\",\n\t\t\t\tsourceType: \"item\",\n\t\t\t\treason: `Item restricts \"${supportId}\" (e.g., mental math question blocks calculator)`,\n\t\t\t\tvalue: ctx.itemSettings.restrictedTools,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// 4. Item requirement (forces enable)\n\t\tif (ctx.itemSettings?.requiredTools?.includes(supportId)) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.mandatedToolIds.add(toolId);\n\t\t\tout.perToolFlags.set(toolId, {\n\t\t\t\trequired: true,\n\t\t\t\talwaysAvailable: false,\n\t\t\t\tsettings: this.resolveToolSettings(supportId, ctx),\n\t\t\t\trule: \"item-requirement\",\n\t\t\t\tsourceType: \"item\",\n\t\t\t});\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 4,\n\t\t\t\trule: \"item-requirement\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"enable\",\n\t\t\t\tsourceType: \"item\",\n\t\t\t\treason: `Item requires \"${supportId}\" for this question`,\n\t\t\t\tvalue: ctx.itemSettings.requiredTools,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// 5. District requirement\n\t\tif (ctx.districtPolicy?.requiredTools?.includes(supportId)) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.mandatedToolIds.add(toolId);\n\t\t\tout.perToolFlags.set(toolId, {\n\t\t\t\trequired: true,\n\t\t\t\talwaysAvailable: false,\n\t\t\t\tsettings: this.resolveToolSettings(supportId, ctx),\n\t\t\t\trule: \"district-requirement\",\n\t\t\t\tsourceType: \"assessment\",\n\t\t\t});\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 5,\n\t\t\t\trule: \"district-requirement\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"enable\",\n\t\t\t\tsourceType: \"assessment\",\n\t\t\t\treason: `District policy requires \"${supportId}\" for all assessments`,\n\t\t\t\tvalue: ctx.districtPolicy.requiredTools,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// 6. PNP prohibitions and supports (student needs)\n\t\tif (ctx.pnp?.prohibitedSupports?.includes(supportId)) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.blockedToolIds.add(toolId);\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 6,\n\t\t\t\trule: \"pnp-prohibited\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"block\",\n\t\t\t\tsourceType: \"student\",\n\t\t\t\treason: `Student PNP profile prohibits \"${supportId}\"`,\n\t\t\t\tvalue: ctx.pnp.prohibitedSupports,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tif (ctx.pnp?.supports?.includes(supportId)) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.perToolFlags.set(toolId, {\n\t\t\t\trequired: false,\n\t\t\t\talwaysAvailable: true,\n\t\t\t\tsettings: this.resolveToolSettings(supportId, ctx),\n\t\t\t\trule: \"pnp-support\",\n\t\t\t\tsourceType: \"student\",\n\t\t\t});\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 6,\n\t\t\t\trule: \"pnp-support\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"enable\",\n\t\t\t\tsourceType: \"student\",\n\t\t\t\treason: `Student PNP profile requests \"${supportId}\"`,\n\t\t\t\tvalue: ctx.pnp.supports,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// 6 (skip): support-id mentioned somewhere but no rule fired.\n\t\t// Use the mapped tool id as `featureId` so the trail is keyed\n\t\t// the same way as every other branch — PR 5's debugger surfaces\n\t\t// index provenance trails by tool id and would otherwise show\n\t\t// orphaned support-id entries here (M8 PR 1 R3 S1).\n\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\tout.decisions.push({\n\t\t\tprecedence: 6,\n\t\t\trule: \"pnp-support\",\n\t\t\tfeatureId: toolId,\n\t\t\taction: \"skip\",\n\t\t\tsourceType: \"system\",\n\t\t\treason: `Feature \"${supportId}\" not configured at any level`,\n\t\t\tvalue: { supportId },\n\t\t});\n\t}\n\n\t/**\n\t * Map a QTI / PNP support id (e.g. `\"calculator-basic\"`) to the\n\t * registered tool id that owns it.\n\t *\n\t * Resolution rules:\n\t *\n\t * 1. **Multiple tools register the same support id:** the\n\t * *first-registered* tool wins. `ToolRegistry.getToolsByPNPSupport`\n\t * returns a `Set<string>` whose iteration order matches the\n\t * `register(...)` insertion order, so the result is\n\t * deterministic across runs but order-sensitive at registration\n\t * time. This is an unusual configuration — the typical case is\n\t * one tool per support id — but it is reachable when an\n\t * integrator overrides a default tool with a replacement that\n\t * claims the same `pnpSupportIds`. In that case the integrator\n\t * should `unregister(...)` the default before registering the\n\t * replacement.\n\t *\n\t * 2. **No tool registers the support id:** the support id is\n\t * returned verbatim as the `featureId` for provenance trails\n\t * and `ToolPolicyEntry.toolId`. This lets hosts use raw QTI\n\t * strings for tools the registry does not (yet) carry without\n\t * losing them in policy evaluation.\n\t *\n\t * The legacy `PnpToolResolver.resolveTool` had the same behavior;\n\t * see `tests/policy/PnpPolicySource.test.ts` for the regression\n\t * lock.\n\t */\n\tprivate mapSupportToToolId(supportId: string): string {\n\t\tconst toolIds = this.toolRegistry.getToolsByPNPSupport(supportId);\n\t\tif (toolIds.size === 0) return supportId;\n\t\treturn Array.from(toolIds)[0];\n\t}\n\n\t/**\n\t * Resolve tool-specific settings, item-level taking precedence over\n\t * assessment-level.\n\t *\n\t * Uses `??` (nullish-coalescing) rather than the legacy resolver's\n\t * `||`. Tool config values are typed `unknown` and may legitimately\n\t * be `false` / `0` / `\"\"` (e.g. a tool whose canonical \"off\" config\n\t * is the literal `false`). `||` would treat those as missing and\n\t * silently fall through to the assessment-level config, masking the\n\t * authored item-level intent. `??` only falls through on\n\t * `null`/`undefined`, which matches the semantic of \"value not\n\t * provided.\" This is a deliberate behavior delta from the legacy\n\t * `PnpToolResolver` (M8 PR 1).\n\t */\n\tprivate resolveToolSettings(\n\t\tsupportId: string,\n\t\tctx: PnpResolutionContext,\n\t): unknown {\n\t\treturn (\n\t\t\tctx.itemSettings?.toolParameters?.[supportId] ??\n\t\t\tctx.toolConfigs?.[supportId]\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"PnpPolicySource.js","sourceRoot":"","sources":["../../../src/policy/sources/PnpPolicySource.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * PNP Policy Source for the M8 engine\n * (see `.cursor/plans/m8-design.md` § 3 step 5 and § 5).\n *\n * This source applies the PNP/profile precedence rules as a `(candidates, pnpPolicyInputs) →\n * (refinedCandidates, perToolFlags, mandates, decisions)` function the\n * engine can call once per `decide(...)` request, with all results\n * routed through the unified `ToolPolicyProvenanceBuilder`.\n */\n\nimport type {\n\tAssessmentEntity,\n\tAssessmentItemRef,\n\tAssessmentSettings,\n\tItemSettings,\n\tPersonalNeedsProfile,\n} from \"@pie-players/pie-players-shared/types\";\n\nimport type { ToolRegistry } from \"../../services/ToolRegistry.js\";\nimport type {\n\tToolPolicyResolutionDecision,\n\tToolPolicySourceType,\n} from \"../core/provenance.js\";\nimport type { PnpPolicySourceRule } from \"../core/policy-source-tag.js\";\n\n/** Per-tool flags PNP/profile policy may attach to a surviving entry. */\nexport interface PnpPolicyToolFlags {\n\t/** PNP/profile policy mandates this tool (item or district `requiredTools`). */\n\trequired: boolean;\n\t/** Student PNP marks this tool as a support (host UI cannot toggle off). */\n\talwaysAvailable: boolean;\n\t/** Tool-specific settings derived from item / assessment settings. */\n\tsettings?: unknown;\n\t/** Which PNP/profile rule contributed the surviving verdict. */\n\trule: PnpPolicySourceRule;\n\t/** Source attribution for `ToolPolicyEntry.sources`. */\n\tsourceType: ToolPolicySourceType;\n}\n\nexport interface PnpPolicyApplyArgs {\n\tassessment?: AssessmentEntity | null;\n\tcurrentItemRef?: AssessmentItemRef;\n}\n\n/**\n * Provenance event the engine should append to its builder. Pre-baked\n * here so the source has a single allocation pattern and the engine\n * keeps a uniform `addDecision(...)` shape.\n */\nexport interface PnpPolicyDecisionEvent {\n\tprecedence: 1 | 2 | 3 | 4 | 5 | 6;\n\trule:\n\t\t| \"district-block\"\n\t\t| \"test-admin-override\"\n\t\t| \"item-restriction\"\n\t\t| \"item-requirement\"\n\t\t| \"district-requirement\"\n\t\t| \"pnp-support\"\n\t\t| \"pnp-prohibited\";\n\tfeatureId: string;\n\taction: ToolPolicyResolutionDecision[\"action\"];\n\tsourceType: ToolPolicySourceType;\n\treason: string;\n\tvalue?: unknown;\n}\n\nexport interface PnpPolicyResult {\n\t/**\n\t * Tool IDs PNP/profile policy explicitly blocked. Engine removes these from the\n\t * candidate set in step 5.\n\t */\n\tblockedToolIds: Set<string>;\n\t/**\n\t * Tool IDs PNP/profile policy mandates (item or district `requiredTools`). Used\n\t * by the engine to detect `tool-policy.requiredToolBlocked`\n\t * diagnostics for tools removed by host policy.\n\t */\n\tmandatedToolIds: Set<string>;\n\t/**\n\t * Per-tool flags merged into surviving `ToolPolicyEntry`s.\n\t */\n\tperToolFlags: Map<string, PnpPolicyToolFlags>;\n\t/**\n\t * Decision log entries the engine must record. Order matches the\n\t * order rules fired internally (highest precedence first per\n\t * support id).\n\t */\n\tdecisions: PnpPolicyDecisionEvent[];\n\t/** Configuration sources the engine should attach to its provenance. */\n\tsources: {\n\t\tassessment?: { id: string; name: string; config?: unknown };\n\t\tstudent?: { id: string; name: string; config?: unknown };\n\t\titem?: { id: string; name: string; config?: unknown };\n\t};\n}\n\n/**\n * Internal context: every PNP/profile policy input bundled together for the rule\n * evaluation loop.\n */\ninterface PnpResolutionContext {\n\tpnp?: PersonalNeedsProfile;\n\tdistrictPolicy?: AssessmentSettings[\"districtPolicy\"];\n\ttestAdmin?: AssessmentSettings[\"testAdministration\"];\n\titemSettings?: ItemSettings;\n\ttoolConfigs?: AssessmentSettings[\"toolConfigs\"];\n}\n\nexport class PnpPolicySource {\n\treadonly id = \"pnp\";\n\tprivate readonly toolRegistry: ToolRegistry;\n\n\tconstructor(toolRegistry: ToolRegistry) {\n\t\tthis.toolRegistry = toolRegistry;\n\t}\n\n\tapply(args: PnpPolicyApplyArgs): PnpPolicyResult {\n\t\tconst { assessment, currentItemRef } = args;\n\t\tconst pnp = assessment?.personalNeedsProfile;\n\t\tconst settings = assessment?.settings as AssessmentSettings | undefined;\n\t\tconst itemSettings = currentItemRef?.settings as ItemSettings | undefined;\n\n\t\tconst result: PnpPolicyResult = {\n\t\t\tblockedToolIds: new Set(),\n\t\t\tmandatedToolIds: new Set(),\n\t\t\tperToolFlags: new Map(),\n\t\t\tdecisions: [],\n\t\t\tsources: {},\n\t\t};\n\n\t\tif (settings?.districtPolicy || settings?.testAdministration) {\n\t\t\tresult.sources.assessment = {\n\t\t\t\tid: assessment?.id || \"unknown\",\n\t\t\t\tname: assessment?.name || assessment?.id || \"Assessment\",\n\t\t\t\tconfig: settings,\n\t\t\t};\n\t\t}\n\t\tif (pnp) {\n\t\t\tresult.sources.student = {\n\t\t\t\tid: \"student\",\n\t\t\t\tname: \"Student PNP Profile\",\n\t\t\t\tconfig: pnp,\n\t\t\t};\n\t\t}\n\t\tif (currentItemRef && itemSettings) {\n\t\t\tresult.sources.item = {\n\t\t\t\tid: currentItemRef.identifier,\n\t\t\t\tname: currentItemRef.identifier,\n\t\t\t\tconfig: itemSettings,\n\t\t\t};\n\t\t}\n\n\t\tconst allSupports = new Set<string>();\n\t\tpnp?.supports?.forEach((s) => allSupports.add(s));\n\t\tpnp?.prohibitedSupports?.forEach((s) => allSupports.add(s));\n\t\tsettings?.districtPolicy?.blockedTools?.forEach((s) => allSupports.add(s));\n\t\tsettings?.districtPolicy?.requiredTools?.forEach((s) => allSupports.add(s));\n\t\titemSettings?.requiredTools?.forEach((s) => allSupports.add(s));\n\t\titemSettings?.restrictedTools?.forEach((s) => allSupports.add(s));\n\n\t\tconst ctx: PnpResolutionContext = {\n\t\t\tpnp,\n\t\t\tdistrictPolicy: settings?.districtPolicy,\n\t\t\ttestAdmin: settings?.testAdministration,\n\t\t\titemSettings,\n\t\t\ttoolConfigs: settings?.toolConfigs,\n\t\t};\n\n\t\tfor (const supportId of allSupports) {\n\t\t\tthis.resolveSupport(supportId, ctx, result);\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate resolveSupport(\n\t\tsupportId: string,\n\t\tctx: PnpResolutionContext,\n\t\tout: PnpPolicyResult,\n\t): void {\n\t\t// 1. District block (absolute veto)\n\t\tif (ctx.districtPolicy?.blockedTools?.includes(supportId)) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.blockedToolIds.add(toolId);\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 1,\n\t\t\t\trule: \"district-block\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"block\",\n\t\t\t\tsourceType: \"assessment\",\n\t\t\t\treason: `District policy blocks \"${supportId}\" for all assessments`,\n\t\t\t\tvalue: ctx.districtPolicy.blockedTools,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// 2. Test administration override\n\t\tif (ctx.testAdmin?.toolOverrides?.[supportId] === false) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.blockedToolIds.add(toolId);\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 2,\n\t\t\t\trule: \"test-admin-override\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"block\",\n\t\t\t\tsourceType: \"assessment\",\n\t\t\t\treason: `Test administrator disabled \"${supportId}\" for this session`,\n\t\t\t\tvalue: ctx.testAdmin.toolOverrides,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// 3. Item restriction (per-item block)\n\t\tif (ctx.itemSettings?.restrictedTools?.includes(supportId)) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.blockedToolIds.add(toolId);\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 3,\n\t\t\t\trule: \"item-restriction\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"block\",\n\t\t\t\tsourceType: \"item\",\n\t\t\t\treason: `Item restricts \"${supportId}\" (e.g., mental math question blocks calculator)`,\n\t\t\t\tvalue: ctx.itemSettings.restrictedTools,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// 4. Item requirement (forces enable)\n\t\tif (ctx.itemSettings?.requiredTools?.includes(supportId)) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.mandatedToolIds.add(toolId);\n\t\t\tout.perToolFlags.set(toolId, {\n\t\t\t\trequired: true,\n\t\t\t\talwaysAvailable: false,\n\t\t\t\tsettings: this.resolveToolSettings(supportId, ctx),\n\t\t\t\trule: \"item-requirement\",\n\t\t\t\tsourceType: \"item\",\n\t\t\t});\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 4,\n\t\t\t\trule: \"item-requirement\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"enable\",\n\t\t\t\tsourceType: \"item\",\n\t\t\t\treason: `Item requires \"${supportId}\" for this question`,\n\t\t\t\tvalue: ctx.itemSettings.requiredTools,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// 5. District requirement\n\t\tif (ctx.districtPolicy?.requiredTools?.includes(supportId)) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.mandatedToolIds.add(toolId);\n\t\t\tout.perToolFlags.set(toolId, {\n\t\t\t\trequired: true,\n\t\t\t\talwaysAvailable: false,\n\t\t\t\tsettings: this.resolveToolSettings(supportId, ctx),\n\t\t\t\trule: \"district-requirement\",\n\t\t\t\tsourceType: \"assessment\",\n\t\t\t});\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 5,\n\t\t\t\trule: \"district-requirement\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"enable\",\n\t\t\t\tsourceType: \"assessment\",\n\t\t\t\treason: `District policy requires \"${supportId}\" for all assessments`,\n\t\t\t\tvalue: ctx.districtPolicy.requiredTools,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// 6. PNP prohibitions and supports (student needs)\n\t\tif (ctx.pnp?.prohibitedSupports?.includes(supportId)) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.blockedToolIds.add(toolId);\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 6,\n\t\t\t\trule: \"pnp-prohibited\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"block\",\n\t\t\t\tsourceType: \"student\",\n\t\t\t\treason: `Student PNP profile prohibits \"${supportId}\"`,\n\t\t\t\tvalue: ctx.pnp.prohibitedSupports,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tif (ctx.pnp?.supports?.includes(supportId)) {\n\t\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\t\tout.perToolFlags.set(toolId, {\n\t\t\t\trequired: false,\n\t\t\t\talwaysAvailable: true,\n\t\t\t\tsettings: this.resolveToolSettings(supportId, ctx),\n\t\t\t\trule: \"pnp-support\",\n\t\t\t\tsourceType: \"student\",\n\t\t\t});\n\t\t\tout.decisions.push({\n\t\t\t\tprecedence: 6,\n\t\t\t\trule: \"pnp-support\",\n\t\t\t\tfeatureId: toolId,\n\t\t\t\taction: \"enable\",\n\t\t\t\tsourceType: \"student\",\n\t\t\t\treason: `Student PNP profile requests \"${supportId}\"`,\n\t\t\t\tvalue: ctx.pnp.supports,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// 6 (skip): support-id mentioned somewhere but no rule fired.\n\t\t// Use the mapped tool id as `featureId` so the trail is keyed\n\t\t// the same way as every other branch — PR 5's debugger surfaces\n\t\t// index provenance trails by tool id and would otherwise show\n\t\t// orphaned support-id entries here (M8 PR 1 R3 S1).\n\t\tconst toolId = this.mapSupportToToolId(supportId);\n\t\tout.decisions.push({\n\t\t\tprecedence: 6,\n\t\t\trule: \"pnp-support\",\n\t\t\tfeatureId: toolId,\n\t\t\taction: \"skip\",\n\t\t\tsourceType: \"system\",\n\t\t\treason: `Feature \"${supportId}\" not configured at any level`,\n\t\t\tvalue: { supportId },\n\t\t});\n\t}\n\n\t/**\n\t * Map a QTI / PNP support id (e.g. `\"calculator-basic\"`) to the\n\t * registered tool id that owns it.\n\t *\n\t * Resolution rules:\n\t *\n\t * 1. **Multiple tools register the same support id:** the\n\t * *first-registered* tool wins. `ToolRegistry.getToolsByPNPSupport`\n\t * returns a `Set<string>` whose iteration order matches the\n\t * `register(...)` insertion order, so the result is\n\t * deterministic across runs but order-sensitive at registration\n\t * time. This is an unusual configuration — the typical case is\n\t * one tool per support id — but it is reachable when an\n\t * integrator overrides a default tool with a replacement that\n\t * claims the same `pnpSupportIds`. In that case the integrator\n\t * should `unregister(...)` the default before registering the\n\t * replacement.\n\t *\n\t * 2. **No tool registers the support id:** the support id is\n\t * returned verbatim as the `featureId` for provenance trails\n\t * and `ToolPolicyEntry.toolId`. This lets hosts use raw QTI\n\t * strings for tools the registry does not (yet) carry without\n\t * losing them in policy evaluation.\n\t *\n\t * `tests/policy/PnpPolicySource.test.ts` locks this mapping behavior.\n\t */\n\tprivate mapSupportToToolId(supportId: string): string {\n\t\tconst toolIds = this.toolRegistry.getToolsByPNPSupport(supportId);\n\t\tif (toolIds.size === 0) return supportId;\n\t\treturn Array.from(toolIds)[0];\n\t}\n\n\t/**\n\t * Resolve tool-specific settings, item-level taking precedence over\n\t * assessment-level.\n\t *\n\t * Uses `??` (nullish-coalescing). Tool config values are typed `unknown` and may legitimately\n\t * be `false` / `0` / `\"\"` (e.g. a tool whose canonical \"off\" config\n\t * is the literal `false`). `||` would treat those as missing and\n\t * silently fall through to the assessment-level config, masking the\n\t * authored item-level intent. `??` only falls through on\n\t * `null`/`undefined`, which matches the semantic of \"value not\n\t * provided.\"\n\t */\n\tprivate resolveToolSettings(\n\t\tsupportId: string,\n\t\tctx: PnpResolutionContext,\n\t): unknown {\n\t\treturn (\n\t\t\tctx.itemSettings?.toolParameters?.[supportId] ??\n\t\t\tctx.toolConfigs?.[supportId]\n\t\t);\n\t}\n}\n"]}
@@ -9,13 +9,14 @@
9
9
  * shells in document order, used for canonical-id lookup;
10
10
  * - the **adapter** (`SectionEngineAdapter`) — single I/O seam over
11
11
  * the pure FSM core, fanning outputs to the DOM, framework-error
12
- * bus, legacy events, instrumentation hook, and host subscribers;
12
+ * bus, instrumentation hook, and host subscribers;
13
13
  * - the **resolver** (`resolveSectionEngineRuntimeState`) — the
14
14
  * toolkit-side resolution helper that produces the effective
15
15
  * runtime + tools snapshot a host feeds to the FSM core.
16
16
  *
17
- * **Two callsite generations.** During the M7 migration window the
18
- * facade carries two surface generations side-by-side:
17
+ * The facade exposes an engine-side surface for stage/lifecycle inputs and a
18
+ * controller-facing surface for item registration, session updates, and host
19
+ * controller commands:
19
20
  *
20
21
  * 1. The **engine-side** surface (`attachHost`, `dispatchInput`,
21
22
  * `subscribe`, `getState`, `getEffectiveRuntime`,
@@ -28,28 +29,14 @@
28
29
  * new layout shell) update the adapter's host element via
29
30
  * `setHost(...)`.
30
31
  *
31
- * 2. The **legacy controller-side** surface (`initialize`,
32
+ * 2. The **controller-side** surface (`initialize`,
32
33
  * `register`, `unregister`, `handleContent*`, `updateItemSession`,
33
34
  * `navigateToItem`, `persist`, `hydrate`, `dispose`). The
34
- * `PieAssessmentToolkit.svelte` CE still drives the seed via this
35
- * surface in PR 3 — observable behavior is identical to pre-PR 3.
36
- * PR 6 deletes the toolkit's reliance on this surface; PR 7
37
- * removes the methods that the engine FSM has fully absorbed.
35
+ * `PieAssessmentToolkit.svelte` CE drives section-controller and
36
+ * item-shell events through this surface.
38
37
  *
39
- * The two surfaces share `RuntimeRegistry` (the only piece both
40
- * generations consult) and the resolved `SectionControllerHandle` the
41
- * legacy generation holds inline. The adapter holds its own
42
- * `coordinator-bridge` independently — the legacy `initialize(...)` and
43
- * the engine-side `dispatchInput({ kind: "initialize" })` do **not**
44
- * fight over the coordinator because PR 3 only activates one of them
45
- * per host (legacy from the toolkit CE, engine-side from the PR 5+
46
- * tests and the kernel/toolkit switches).
47
- *
48
- * **Why the legacy surface stays.** The plan's PR-3 acceptance criterion
49
- * is "existing toolkit tests still pass; no section-player or toolkit
50
- * CE code changes yet" — so PR 3 cannot delete the controller-side
51
- * methods. PR 6 (toolkit switch) and PR 7 (rip-out) will remove them
52
- * once the engine surface is the only consumer.
38
+ * The two surfaces share `RuntimeRegistry` and the resolved
39
+ * `SectionControllerHandle`.
53
40
  */
54
41
  import type { ToolkitCoordinator } from "../services/ToolkitCoordinator.js";
55
42
  import type { FrameworkErrorReporter } from "../services/framework-error-bus.js";
@@ -110,10 +97,8 @@ interface RuntimeController extends SectionControllerHandle {
110
97
  navigateToItem?: (index: number) => unknown;
111
98
  }
112
99
  /**
113
- * Legacy initialize args. Pre-PR 6 callers (the toolkit CE) drive the
114
- * coordinator + section controller through this method. PR 6 will
115
- * delete this in favor of the engine-side `attachHost` +
116
- * `dispatchInput({ kind: "initialize" })` flow.
100
+ * Initialize args used by the toolkit CE to resolve the coordinator-backed
101
+ * section controller.
117
102
  */
118
103
  interface EngineInitArgs {
119
104
  coordinator: ToolkitCoordinator;
@@ -128,7 +113,7 @@ interface EngineInitArgs {
128
113
  /**
129
114
  * Engine-side `attachHost` args. The host element, framework-error bus,
130
115
  * and `sourceCe` together let the adapter dispatch DOM events and bus
131
- * fan-outs identically to the legacy kernel emit chain.
116
+ * fan-outs consistently with the kernel emit chain.
132
117
  */
133
118
  export interface SectionRuntimeEngineHostArgs {
134
119
  host: EventTarget;
@@ -175,7 +160,7 @@ export declare class SectionRuntimeEngine {
175
160
  /**
176
161
  * Forward a host-constructed input to the FSM core via the adapter.
177
162
  * Returns the outputs the transition produced (subscribers and the
178
- * DOM/legacy/framework-error/instrumentation bridges have already
163
+ * DOM/framework-error/instrumentation bridges have already
179
164
  * received them).
180
165
  *
181
166
  * No-op (returns `[]`) before `attachHost(...)` so callers that
@@ -9,13 +9,14 @@
9
9
  * shells in document order, used for canonical-id lookup;
10
10
  * - the **adapter** (`SectionEngineAdapter`) — single I/O seam over
11
11
  * the pure FSM core, fanning outputs to the DOM, framework-error
12
- * bus, legacy events, instrumentation hook, and host subscribers;
12
+ * bus, instrumentation hook, and host subscribers;
13
13
  * - the **resolver** (`resolveSectionEngineRuntimeState`) — the
14
14
  * toolkit-side resolution helper that produces the effective
15
15
  * runtime + tools snapshot a host feeds to the FSM core.
16
16
  *
17
- * **Two callsite generations.** During the M7 migration window the
18
- * facade carries two surface generations side-by-side:
17
+ * The facade exposes an engine-side surface for stage/lifecycle inputs and a
18
+ * controller-facing surface for item registration, session updates, and host
19
+ * controller commands:
19
20
  *
20
21
  * 1. The **engine-side** surface (`attachHost`, `dispatchInput`,
21
22
  * `subscribe`, `getState`, `getEffectiveRuntime`,
@@ -28,28 +29,14 @@
28
29
  * new layout shell) update the adapter's host element via
29
30
  * `setHost(...)`.
30
31
  *
31
- * 2. The **legacy controller-side** surface (`initialize`,
32
+ * 2. The **controller-side** surface (`initialize`,
32
33
  * `register`, `unregister`, `handleContent*`, `updateItemSession`,
33
34
  * `navigateToItem`, `persist`, `hydrate`, `dispose`). The
34
- * `PieAssessmentToolkit.svelte` CE still drives the seed via this
35
- * surface in PR 3 — observable behavior is identical to pre-PR 3.
36
- * PR 6 deletes the toolkit's reliance on this surface; PR 7
37
- * removes the methods that the engine FSM has fully absorbed.
35
+ * `PieAssessmentToolkit.svelte` CE drives section-controller and
36
+ * item-shell events through this surface.
38
37
  *
39
- * The two surfaces share `RuntimeRegistry` (the only piece both
40
- * generations consult) and the resolved `SectionControllerHandle` the
41
- * legacy generation holds inline. The adapter holds its own
42
- * `coordinator-bridge` independently — the legacy `initialize(...)` and
43
- * the engine-side `dispatchInput({ kind: "initialize" })` do **not**
44
- * fight over the coordinator because PR 3 only activates one of them
45
- * per host (legacy from the toolkit CE, engine-side from the PR 5+
46
- * tests and the kernel/toolkit switches).
47
- *
48
- * **Why the legacy surface stays.** The plan's PR-3 acceptance criterion
49
- * is "existing toolkit tests still pass; no section-player or toolkit
50
- * CE code changes yet" — so PR 3 cannot delete the controller-side
51
- * methods. PR 6 (toolkit switch) and PR 7 (rip-out) will remove them
52
- * once the engine surface is the only consumer.
38
+ * The two surfaces share `RuntimeRegistry` and the resolved
39
+ * `SectionControllerHandle`.
53
40
  */
54
41
  import { SectionEngineAdapter, } from "./adapter/SectionEngineAdapter.js";
55
42
  import { resolveSectionEngineRuntimeState, } from "./core/engine-resolver.js";
@@ -61,7 +48,7 @@ export class SectionRuntimeEngine {
61
48
  runtimeId = createRuntimeId("section-engine");
62
49
  // Engine-side state (PR 3+).
63
50
  adapter = null;
64
- // Legacy controller-side state (until PR 6).
51
+ // Controller-side state.
65
52
  controller = null;
66
53
  coordinator = null;
67
54
  sectionId = "";
@@ -116,7 +103,7 @@ export class SectionRuntimeEngine {
116
103
  /**
117
104
  * Forward a host-constructed input to the FSM core via the adapter.
118
105
  * Returns the outputs the transition produced (subscribers and the
119
- * DOM/legacy/framework-error/instrumentation bridges have already
106
+ * DOM/framework-error/instrumentation bridges have already
120
107
  * received them).
121
108
  *
122
109
  * No-op (returns `[]`) before `attachHost(...)` so callers that
@@ -168,8 +155,7 @@ export class SectionRuntimeEngine {
168
155
  return this.runtimeId;
169
156
  }
170
157
  // ============================================================
171
- // Legacy controller-side surface (kept until PR 6; behavior
172
- // identical to pre-PR 3).
158
+ // Controller-side surface.
173
159
  // ============================================================
174
160
  async initialize(args) {
175
161
  this.activeInitToken += 1;
@@ -321,7 +307,7 @@ export class SectionRuntimeEngine {
321
307
  // delete keys round-trip identically. The `|| detail.kind`
322
308
  // fallback is preserved for the controller-forwarded payload
323
309
  // because the controller normalizes via `toSectionContentKind`
324
- // and the legacy `kind` field is still meaningful there.
310
+ // and the `kind` field remains meaningful there.
325
311
  this.loadedRenderableKeys.delete(this.getLoadedKey(canonicalItemId, detail.contentKind));
326
312
  this.controller?.handleContentUnregistered?.({
327
313
  itemId: detail.itemId,
@@ -1 +1 @@
1
- {"version":3,"file":"SectionRuntimeEngine.js","sourceRoot":"","sources":["../../src/runtime/SectionRuntimeEngine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Section runtime engine — public facade (M7 — Variant C, layered).\n *\n * The facade is the narrow, stable entry point hosts use to mount,\n * drive, and dispose a section runtime. It composes three layers\n * introduced in earlier M7 PRs:\n *\n * - the **registry** (`RuntimeRegistry`) — registered item/passage\n * shells in document order, used for canonical-id lookup;\n * - the **adapter** (`SectionEngineAdapter`) — single I/O seam over\n * the pure FSM core, fanning outputs to the DOM, framework-error\n * bus, legacy events, instrumentation hook, and host subscribers;\n * - the **resolver** (`resolveSectionEngineRuntimeState`) — the\n * toolkit-side resolution helper that produces the effective\n * runtime + tools snapshot a host feeds to the FSM core.\n *\n * **Two callsite generations.** During the M7 migration window the\n * facade carries two surface generations side-by-side:\n *\n * 1. The **engine-side** surface (`attachHost`, `dispatchInput`,\n * `subscribe`, `getState`, `getEffectiveRuntime`,\n * `setInstrumentationHook`). This is what the kernel and the\n * `pie-assessment-toolkit` CE will switch onto in M7 PR 5 and\n * PR 6. Construction is lazy: `attachHost(...)` builds the\n * adapter on first call, with the supplied host element,\n * framework-error bus, and (optional) coordinator. After\n * construction, host re-attachments (e.g. cohort change with a\n * new layout shell) update the adapter's host element via\n * `setHost(...)`.\n *\n * 2. The **legacy controller-side** surface (`initialize`,\n * `register`, `unregister`, `handleContent*`, `updateItemSession`,\n * `navigateToItem`, `persist`, `hydrate`, `dispose`). The\n * `PieAssessmentToolkit.svelte` CE still drives the seed via this\n * surface in PR 3 — observable behavior is identical to pre-PR 3.\n * PR 6 deletes the toolkit's reliance on this surface; PR 7\n * removes the methods that the engine FSM has fully absorbed.\n *\n * The two surfaces share `RuntimeRegistry` (the only piece both\n * generations consult) and the resolved `SectionControllerHandle` the\n * legacy generation holds inline. The adapter holds its own\n * `coordinator-bridge` independently — the legacy `initialize(...)` and\n * the engine-side `dispatchInput({ kind: \"initialize\" })` do **not**\n * fight over the coordinator because PR 3 only activates one of them\n * per host (legacy from the toolkit CE, engine-side from the PR 5+\n * tests and the kernel/toolkit switches).\n *\n * **Why the legacy surface stays.** The plan's PR-3 acceptance criterion\n * is \"existing toolkit tests still pass; no section-player or toolkit\n * CE code changes yet\" — so PR 3 cannot delete the controller-side\n * methods. PR 6 (toolkit switch) and PR 7 (rip-out) will remove them\n * once the engine surface is the only consumer.\n */\n\nimport type { ToolkitCoordinator } from \"../services/ToolkitCoordinator.js\";\nimport type { FrameworkErrorReporter } from \"../services/framework-error-bus.js\";\nimport type {\n\tSectionControllerEvent,\n\tSectionControllerHandle,\n} from \"../services/section-controller-types.js\";\nimport {\n\tSectionEngineAdapter,\n\ttype SectionEngineAdapterOptions,\n} from \"./adapter/SectionEngineAdapter.js\";\nimport type { InstrumentationHook } from \"./adapter/instrumentation-bridge.js\";\nimport type { EngineOutputListener } from \"./adapter/subscriber-fanout.js\";\nimport type { SectionEngineInput } from \"./core/engine-input.js\";\nimport type { SectionEngineOutput } from \"./core/engine-output.js\";\nimport {\n\tresolveSectionEngineRuntimeState,\n\ttype EffectiveRuntime,\n\ttype RuntimeInputs,\n} from \"./core/engine-resolver.js\";\nimport {\n\tcreateInitialEngineState,\n\ttype SectionEngineState,\n} from \"./core/engine-state.js\";\nimport type { RuntimeRegistrationDetail } from \"./registration-events.js\";\nimport { RuntimeRegistry } from \"./RuntimeRegistry.js\";\nimport { createRuntimeId } from \"./runtime-id.js\";\n\n/**\n * Structural view of the resolved section controller, widened with the\n * extra methods the toolkit's runtime CE calls directly. Optional so\n * stub controllers used in tests do not need every entry point.\n */\ninterface RuntimeController extends SectionControllerHandle {\n\tgetCompositionModel?: () => unknown;\n\tgetCanonicalItemId?: (itemId: string) => string;\n\thandleContentLoaded?: (args: {\n\t\titemId: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t\tdetail?: unknown;\n\t\ttimestamp?: number;\n\t}) => void;\n\thandleContentRegistered?: (args: {\n\t\titemId: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t}) => void;\n\thandleContentUnregistered?: (args: {\n\t\titemId: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t}) => void;\n\thandleItemPlayerError?: (args: {\n\t\titemId: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t\terror: unknown;\n\t\ttimestamp?: number;\n\t}) => void;\n\treportSectionError?: (args: {\n\t\tsource: \"item-player\" | \"section-runtime\" | \"toolkit\" | \"controller\";\n\t\terror: unknown;\n\t\titemId?: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t\ttimestamp?: number;\n\t}) => void;\n\tupdateItemSession?: (\n\t\titemId: string,\n\t\tsessionDetail: unknown,\n\t) => { eventDetail?: unknown } | null;\n\tsubscribe?: (listener: (event: SectionControllerEvent) => void) => () => void;\n\tnavigateToItem?: (index: number) => unknown;\n}\n\n/**\n * Legacy initialize args. Pre-PR 6 callers (the toolkit CE) drive the\n * coordinator + section controller through this method. PR 6 will\n * delete this in favor of the engine-side `attachHost` +\n * `dispatchInput({ kind: \"initialize\" })` flow.\n */\ninterface EngineInitArgs {\n\tcoordinator: ToolkitCoordinator;\n\tsection: unknown;\n\tsectionId: string;\n\tassessmentId: string;\n\tview: string;\n\tattemptId?: string;\n\tcreateDefaultController: () => Promise<RuntimeController> | RuntimeController;\n\tonCompositionChanged?: (composition: unknown) => void;\n}\n\n/**\n * Engine-side `attachHost` args. The host element, framework-error bus,\n * and `sourceCe` together let the adapter dispatch DOM events and bus\n * fan-outs identically to the legacy kernel emit chain.\n */\nexport interface SectionRuntimeEngineHostArgs {\n\thost: EventTarget;\n\tsourceCe: string;\n\tframeworkErrorBus: FrameworkErrorReporter;\n\tcoordinator?: SectionEngineAdapterOptions[\"coordinator\"];\n\tinstrumentationHook?: InstrumentationHook;\n\tnow?: () => string;\n}\n\nexport class SectionRuntimeEngine {\n\tprivate readonly registry = new RuntimeRegistry();\n\tprivate readonly runtimeId = createRuntimeId(\"section-engine\");\n\n\t// Engine-side state (PR 3+).\n\tprivate adapter: SectionEngineAdapter | null = null;\n\n\t// Legacy controller-side state (until PR 6).\n\tprivate controller: RuntimeController | null = null;\n\tprivate coordinator: ToolkitCoordinator | null = null;\n\tprivate sectionId = \"\";\n\tprivate attemptId: string | undefined;\n\tprivate unsubscribeController: (() => void) | null = null;\n\tprivate activeInitToken = 0;\n\n\t/**\n\t * Tracks which `(canonicalItemId, contentKind)` pairs have already\n\t * fired `handleContentLoaded` on this engine. Mirrored alongside\n\t * `RuntimeRegistry`'s registered set so a cohort handoff can\n\t * replay the persistent shells' loaded state into the new\n\t * controller — see `replayRegisteredShellsIntoController` and the\n\t * PIE-512 Phase B regression test.\n\t *\n\t * Cleared in lockstep with `handleContentUnregistered` and on\n\t * `dispose`. Not exposed publicly: the registry alone is the\n\t * external surface; the loaded set exists only to seed cohort B's\n\t * controller with the live state cohort A's shells produced.\n\t */\n\tprivate readonly loadedRenderableKeys = new Set<string>();\n\n\t// ============================================================\n\t// Engine-side surface (PR 3+; driven by the kernel from PR 5,\n\t// by the toolkit CE from PR 6, and by the facade smoke test today).\n\t// ============================================================\n\n\t/**\n\t * Attach the host element + framework-error bus + sourceCe so the\n\t * adapter can dispatch DOM events and bus errors. Required before\n\t * `dispatchInput` / `subscribe` produce observable effects.\n\t *\n\t * Constructs the adapter on the first call. Subsequent calls keep\n\t * the adapter and update its host element / instrumentation hook\n\t * (used by layout-shell rollovers and instrumentation reconfig).\n\t */\n\tattachHost(args: SectionRuntimeEngineHostArgs): void {\n\t\tif (this.adapter) {\n\t\t\tthis.adapter.setHost(args.host);\n\t\t\tif (args.instrumentationHook !== undefined) {\n\t\t\t\tthis.adapter.setInstrumentationHook(args.instrumentationHook);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tthis.adapter = new SectionEngineAdapter({\n\t\t\thost: args.host,\n\t\t\truntimeId: this.runtimeId,\n\t\t\tsourceCe: args.sourceCe,\n\t\t\tframeworkErrorBus: args.frameworkErrorBus,\n\t\t\tcoordinator: args.coordinator,\n\t\t\tinstrumentationHook: args.instrumentationHook,\n\t\t\tnow: args.now,\n\t\t});\n\t}\n\n\t/**\n\t * Forward a host-constructed input to the FSM core via the adapter.\n\t * Returns the outputs the transition produced (subscribers and the\n\t * DOM/legacy/framework-error/instrumentation bridges have already\n\t * received them).\n\t *\n\t * No-op (returns `[]`) before `attachHost(...)` so callers that\n\t * dispatch optimistically during teardown do not throw.\n\t */\n\tdispatchInput(input: SectionEngineInput): readonly SectionEngineOutput[] {\n\t\treturn this.adapter?.dispatchInput(input) ?? [];\n\t}\n\n\t/**\n\t * Subscribe to batched engine outputs (one batch per\n\t * `dispatchInput`). Returns an idempotent disposer. Pre-`attachHost`\n\t * subscriptions return a no-op disposer so callers can wire\n\t * subscriptions early without lifecycle-ordering hazards.\n\t */\n\tsubscribe(listener: EngineOutputListener): () => void {\n\t\treturn this.adapter?.subscribe(listener) ?? noopDispose;\n\t}\n\n\t/**\n\t * Read-only snapshot of the FSM state. Returns the initial idle\n\t * state before `attachHost` so callers can probe state without\n\t * special-casing the pre-attach phase.\n\t */\n\tgetState(): Readonly<SectionEngineState> {\n\t\treturn this.adapter?.getState() ?? createInitialEngineState();\n\t}\n\n\t/**\n\t * Resolve the effective runtime + tools snapshot for the given\n\t * two-tier inputs. This is a pure function over the resolver — it\n\t * does not depend on adapter state — so callers can use it both\n\t * pre- and post-`attachHost`. The kernel uses it to seed\n\t * `dispatchInput({ kind: \"initialize\", effectiveRuntime, ... })`.\n\t */\n\tgetEffectiveRuntime<P>(\n\t\targs: RuntimeInputs,\n\t\tdeps: {\n\t\t\tresolvePlayerRuntime: (resolverArgs: {\n\t\t\t\teffectiveRuntime: Record<string, unknown>;\n\t\t\t\tplayerType: string;\n\t\t\t\tenv: Record<string, unknown> | null;\n\t\t\t}) => P;\n\t\t},\n\t): EffectiveRuntime {\n\t\treturn resolveSectionEngineRuntimeState(args, deps).effectiveRuntime;\n\t}\n\n\t/**\n\t * Update the adapter's instrumentation hook. No-op pre-attach.\n\t * Hosts that always want a hook should pass it via `attachHost`.\n\t */\n\tsetInstrumentationHook(hook: InstrumentationHook | undefined): void {\n\t\tthis.adapter?.setInstrumentationHook(hook);\n\t}\n\n\t/**\n\t * Stable per-engine runtime id used to scope DOM-event details and\n\t * registry telemetry. Exposed so tests and instrumentation can pin\n\t * the value the bridges emit.\n\t */\n\tgetRuntimeId(): string {\n\t\treturn this.runtimeId;\n\t}\n\n\t// ============================================================\n\t// Legacy controller-side surface (kept until PR 6; behavior\n\t// identical to pre-PR 3).\n\t// ============================================================\n\n\tasync initialize(args: EngineInitArgs): Promise<void> {\n\t\tthis.activeInitToken += 1;\n\t\tconst token = this.activeInitToken;\n\t\tthis.coordinator = args.coordinator;\n\t\tthis.sectionId = args.sectionId;\n\t\tthis.attemptId = args.attemptId;\n\n\t\tconst resolved = (await args.coordinator.getOrCreateSectionController({\n\t\t\tsectionId: args.sectionId,\n\t\t\tattemptId: args.attemptId,\n\t\t\tinput: {\n\t\t\t\tsection: args.section,\n\t\t\t\tsectionId: args.sectionId,\n\t\t\t\tassessmentId: args.assessmentId,\n\t\t\t\tview: args.view,\n\t\t\t},\n\t\t\tupdateExisting: true,\n\t\t\tcreateDefaultController: args.createDefaultController,\n\t\t})) as RuntimeController;\n\n\t\tif (token !== this.activeInitToken) return;\n\t\tthis.unsubscribeController?.();\n\t\tthis.controller = resolved;\n\t\t// PIE-512 Phase C: always re-feed the registry's currently\n\t\t// registered shells (and their loaded state) into the resolved\n\t\t// controller. We do NOT gate on `resolved !== previousController`\n\t\t// any more.\n\t\t//\n\t\t// Why the gate had to go:\n\t\t// - Cohort flip resolving to a fresh controller — replay seeds\n\t\t// the new controller, which is the original Phase B fix.\n\t\t// - Same-cohort `updateInput` resolving to the existing\n\t\t// controller — the coordinator's\n\t\t// `resolveExistingSectionController` calls\n\t\t// `existingController.updateInput(input)` (engine always\n\t\t// passes `updateExisting: true`), and pre-Phase-C\n\t\t// `SectionController.initialize` wiped lifecycle tracking\n\t\t// unconditionally. A subscriber attaching between the wipe\n\t\t// and the next live event saw an empty\n\t\t// `loadedRenderables` snapshot.\n\t\t//\n\t\t// Phase C makes this re-feed safe by combining (a) the\n\t\t// section-identity gate around `resetLifecycleTracking()` in\n\t\t// `SectionController.initialize` (so same-cohort `updateInput`\n\t\t// preserves tracking) with (b) idempotent\n\t\t// `handleContentRegistered` / `handleContentLoaded` on the\n\t\t// controller (so re-feeding the same registry entries is a\n\t\t// no-op if the controller already knows about them, but\n\t\t// re-seeds the controller in the post-`updateInput`-wipe case\n\t\t// that pre-Phase-C used to encounter).\n\t\tthis.replayRegisteredShellsIntoController(resolved);\n\t\targs.onCompositionChanged?.(resolved.getCompositionModel?.());\n\t\tthis.unsubscribeController =\n\t\t\tresolved.subscribe?.(() => {\n\t\t\t\targs.onCompositionChanged?.(resolved.getCompositionModel?.());\n\t\t\t}) || null;\n\t}\n\n\t/**\n\t * Re-feed the engine's `RuntimeRegistry` and loaded-set into the\n\t * resolved controller. Call site: `initialize(...)` — runs on\n\t * EVERY initialize, both cohort-flip-resolves-fresh-controller\n\t * and same-cohort-resolves-existing-controller cases. Phase C\n\t * dropped the `resolved !== previousController` gate; see the\n\t * comment at the call site for why.\n\t *\n\t * Two-pass order — register every shell in document order, then\n\t * issue `handleContentLoaded` for each shell whose load already\n\t * fired on this engine. The two-pass shape prevents\n\t * `evaluateSectionLoadingState` from flapping\n\t * `section-loading-complete` `false→true→false→…` while replay is\n\t * mid-walk: any subscriber wired before the engine's own\n\t * `controller.subscribe` (no such caller today, but cheap defense\n\t * for future wiring) sees one clean `false→true` transition.\n\t *\n\t * Idempotent on the controller side — Phase C makes\n\t * `SectionController.handleContentRegistered` and\n\t * `handleContentLoaded` early-return on duplicates so re-feeding\n\t * the same shells into a controller that already tracks them is\n\t * a true no-op (no spurious re-emits, no re-evaluation of\n\t * `section-loading-complete`). At the call site no listener is\n\t * attached during the replay window, so `emitChange` side effects\n\t * fire into a void on this pass anyway, but the controller-side\n\t * idempotence is what makes the replay safe to run on every\n\t * initialize regardless of whether the controller is fresh.\n\t */\n\tprivate replayRegisteredShellsIntoController(\n\t\tcontroller: RuntimeController,\n\t): void {\n\t\tif (!controller.handleContentRegistered) return;\n\t\tconst shells = this.registry.getOrderedShells();\n\t\tif (shells.length === 0) return;\n\t\tfor (const shell of shells) {\n\t\t\tconst canonicalItemId = shell.canonicalItemId || shell.itemId;\n\t\t\tcontroller.handleContentRegistered({\n\t\t\t\titemId: shell.itemId,\n\t\t\t\tcanonicalItemId,\n\t\t\t\tcontentKind: shell.contentKind || shell.kind,\n\t\t\t});\n\t\t}\n\t\tconst now = Date.now();\n\t\tfor (const shell of shells) {\n\t\t\tconst canonicalItemId = shell.canonicalItemId || shell.itemId;\n\t\t\tconst key = this.getLoadedKey(canonicalItemId, shell.contentKind);\n\t\t\tif (this.loadedRenderableKeys.has(key)) {\n\t\t\t\tcontroller.handleContentLoaded?.({\n\t\t\t\t\titemId: shell.itemId,\n\t\t\t\t\tcanonicalItemId,\n\t\t\t\t\tcontentKind: shell.contentKind || shell.kind,\n\t\t\t\t\ttimestamp: now,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Stable key for the engine's `loadedRenderableKeys` set. Mirrors\n\t * the `(canonicalItemId, contentKind)` shape `SectionController`\n\t * uses internally (see `getRenderableKey`); we only need\n\t * consistent add/check semantics on this side, not byte-identical\n\t * normalization with the controller.\n\t */\n\tprivate getLoadedKey(\n\t\tcanonicalItemId: string,\n\t\tcontentKind: string | undefined,\n\t): string {\n\t\treturn `${contentKind ?? \"\"}:${canonicalItemId}`;\n\t}\n\n\tregister(detail: RuntimeRegistrationDetail): boolean {\n\t\treturn this.registry.register(detail);\n\t}\n\n\tunregister(element: HTMLElement): boolean {\n\t\treturn this.registry.unregister(element);\n\t}\n\n\tgetCompositionModel(): unknown {\n\t\treturn this.controller?.getCompositionModel?.() ?? null;\n\t}\n\n\tgetCanonicalItemId(itemId: string): string {\n\t\tconst map = this.registry.getCanonicalIdMap();\n\t\treturn map[itemId] || this.controller?.getCanonicalItemId?.(itemId) || itemId;\n\t}\n\n\thandleContentRegistered(detail: RuntimeRegistrationDetail): void {\n\t\tthis.controller?.handleContentRegistered?.({\n\t\t\titemId: detail.itemId,\n\t\t\tcanonicalItemId: detail.canonicalItemId || detail.itemId,\n\t\t\tcontentKind: detail.contentKind || detail.kind,\n\t\t});\n\t}\n\n\thandleContentUnregistered(detail: RuntimeRegistrationDetail): void {\n\t\tconst canonicalItemId = detail.canonicalItemId || detail.itemId;\n\t\t// Key the load-set delete with the same `contentKind`-only\n\t\t// shape that `handleContentLoaded` and\n\t\t// `replayRegisteredShellsIntoController` use, so add/check/\n\t\t// delete keys round-trip identically. The `|| detail.kind`\n\t\t// fallback is preserved for the controller-forwarded payload\n\t\t// because the controller normalizes via `toSectionContentKind`\n\t\t// and the legacy `kind` field is still meaningful there.\n\t\tthis.loadedRenderableKeys.delete(\n\t\t\tthis.getLoadedKey(canonicalItemId, detail.contentKind),\n\t\t);\n\t\tthis.controller?.handleContentUnregistered?.({\n\t\t\titemId: detail.itemId,\n\t\t\tcanonicalItemId,\n\t\t\tcontentKind: detail.contentKind || detail.kind,\n\t\t});\n\t}\n\n\thandleContentLoaded(args: {\n\t\titemId: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t\tdetail?: unknown;\n\t\ttimestamp?: number;\n\t}): void {\n\t\tconst canonicalItemId = args.canonicalItemId || args.itemId;\n\t\tthis.loadedRenderableKeys.add(\n\t\t\tthis.getLoadedKey(canonicalItemId, args.contentKind),\n\t\t);\n\t\tthis.controller?.handleContentLoaded?.(args);\n\t}\n\n\thandleItemPlayerError(args: {\n\t\titemId: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t\terror: unknown;\n\t\ttimestamp?: number;\n\t}): void {\n\t\tthis.controller?.handleItemPlayerError?.(args);\n\t}\n\n\treportSectionError(args: {\n\t\tsource: \"item-player\" | \"section-runtime\" | \"toolkit\" | \"controller\";\n\t\terror: unknown;\n\t\titemId?: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t\ttimestamp?: number;\n\t}): void {\n\t\tthis.controller?.reportSectionError?.(args);\n\t}\n\n\tupdateItemSession(itemId: string, session: unknown): unknown {\n\t\tconst canonicalId = this.getCanonicalItemId(itemId);\n\t\treturn this.controller?.updateItemSession?.(canonicalId, session) ?? null;\n\t}\n\n\tnavigateToItem(index: number): unknown {\n\t\treturn this.controller?.navigateToItem?.(index) ?? null;\n\t}\n\n\tasync persist(): Promise<void> {\n\t\tawait this.controller?.persist?.();\n\t}\n\n\tasync hydrate(): Promise<void> {\n\t\tawait this.controller?.hydrate?.();\n\t}\n\n\tgetRegistry(): RuntimeRegistry {\n\t\treturn this.registry;\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tthis.activeInitToken += 1;\n\t\tthis.unsubscribeController?.();\n\t\tthis.unsubscribeController = null;\n\t\tif (this.coordinator && this.sectionId) {\n\t\t\tawait this.coordinator.disposeSectionController({\n\t\t\t\tsectionId: this.sectionId,\n\t\t\t\tattemptId: this.attemptId,\n\t\t\t});\n\t\t}\n\t\tthis.registry.clear();\n\t\tthis.loadedRenderableKeys.clear();\n\t\tthis.controller = null;\n\t\tthis.coordinator = null;\n\t\t// Tear down the engine-side adapter (if attached) last so any\n\t\t// `dispose` FSM input flows through the bridges before they\n\t\t// detach. Adapter disposal is idempotent and safe even when\n\t\t// nothing is attached.\n\t\tif (this.adapter) {\n\t\t\tconst adapter = this.adapter;\n\t\t\tthis.adapter = null;\n\t\t\tawait adapter.dispose();\n\t\t}\n\t}\n}\n\nconst noopDispose = (): void => {};\n"]}
1
+ {"version":3,"file":"SectionRuntimeEngine.js","sourceRoot":"","sources":["../../src/runtime/SectionRuntimeEngine.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Section runtime engine — public facade (M7 — Variant C, layered).\n *\n * The facade is the narrow, stable entry point hosts use to mount,\n * drive, and dispose a section runtime. It composes three layers\n * introduced in earlier M7 PRs:\n *\n * - the **registry** (`RuntimeRegistry`) — registered item/passage\n * shells in document order, used for canonical-id lookup;\n * - the **adapter** (`SectionEngineAdapter`) — single I/O seam over\n * the pure FSM core, fanning outputs to the DOM, framework-error\n * bus, instrumentation hook, and host subscribers;\n * - the **resolver** (`resolveSectionEngineRuntimeState`) — the\n * toolkit-side resolution helper that produces the effective\n * runtime + tools snapshot a host feeds to the FSM core.\n *\n * The facade exposes an engine-side surface for stage/lifecycle inputs and a\n * controller-facing surface for item registration, session updates, and host\n * controller commands:\n *\n * 1. The **engine-side** surface (`attachHost`, `dispatchInput`,\n * `subscribe`, `getState`, `getEffectiveRuntime`,\n * `setInstrumentationHook`). This is what the kernel and the\n * `pie-assessment-toolkit` CE will switch onto in M7 PR 5 and\n * PR 6. Construction is lazy: `attachHost(...)` builds the\n * adapter on first call, with the supplied host element,\n * framework-error bus, and (optional) coordinator. After\n * construction, host re-attachments (e.g. cohort change with a\n * new layout shell) update the adapter's host element via\n * `setHost(...)`.\n *\n * 2. The **controller-side** surface (`initialize`,\n * `register`, `unregister`, `handleContent*`, `updateItemSession`,\n * `navigateToItem`, `persist`, `hydrate`, `dispose`). The\n * `PieAssessmentToolkit.svelte` CE drives section-controller and\n * item-shell events through this surface.\n *\n * The two surfaces share `RuntimeRegistry` and the resolved\n * `SectionControllerHandle`.\n */\n\nimport type { ToolkitCoordinator } from \"../services/ToolkitCoordinator.js\";\nimport type { FrameworkErrorReporter } from \"../services/framework-error-bus.js\";\nimport type {\n\tSectionControllerEvent,\n\tSectionControllerHandle,\n} from \"../services/section-controller-types.js\";\nimport {\n\tSectionEngineAdapter,\n\ttype SectionEngineAdapterOptions,\n} from \"./adapter/SectionEngineAdapter.js\";\nimport type { InstrumentationHook } from \"./adapter/instrumentation-bridge.js\";\nimport type { EngineOutputListener } from \"./adapter/subscriber-fanout.js\";\nimport type { SectionEngineInput } from \"./core/engine-input.js\";\nimport type { SectionEngineOutput } from \"./core/engine-output.js\";\nimport {\n\tresolveSectionEngineRuntimeState,\n\ttype EffectiveRuntime,\n\ttype RuntimeInputs,\n} from \"./core/engine-resolver.js\";\nimport {\n\tcreateInitialEngineState,\n\ttype SectionEngineState,\n} from \"./core/engine-state.js\";\nimport type { RuntimeRegistrationDetail } from \"./registration-events.js\";\nimport { RuntimeRegistry } from \"./RuntimeRegistry.js\";\nimport { createRuntimeId } from \"./runtime-id.js\";\n\n/**\n * Structural view of the resolved section controller, widened with the\n * extra methods the toolkit's runtime CE calls directly. Optional so\n * stub controllers used in tests do not need every entry point.\n */\ninterface RuntimeController extends SectionControllerHandle {\n\tgetCompositionModel?: () => unknown;\n\tgetCanonicalItemId?: (itemId: string) => string;\n\thandleContentLoaded?: (args: {\n\t\titemId: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t\tdetail?: unknown;\n\t\ttimestamp?: number;\n\t}) => void;\n\thandleContentRegistered?: (args: {\n\t\titemId: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t}) => void;\n\thandleContentUnregistered?: (args: {\n\t\titemId: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t}) => void;\n\thandleItemPlayerError?: (args: {\n\t\titemId: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t\terror: unknown;\n\t\ttimestamp?: number;\n\t}) => void;\n\treportSectionError?: (args: {\n\t\tsource: \"item-player\" | \"section-runtime\" | \"toolkit\" | \"controller\";\n\t\terror: unknown;\n\t\titemId?: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t\ttimestamp?: number;\n\t}) => void;\n\tupdateItemSession?: (\n\t\titemId: string,\n\t\tsessionDetail: unknown,\n\t) => { eventDetail?: unknown } | null;\n\tsubscribe?: (listener: (event: SectionControllerEvent) => void) => () => void;\n\tnavigateToItem?: (index: number) => unknown;\n}\n\n/**\n * Initialize args used by the toolkit CE to resolve the coordinator-backed\n * section controller.\n */\ninterface EngineInitArgs {\n\tcoordinator: ToolkitCoordinator;\n\tsection: unknown;\n\tsectionId: string;\n\tassessmentId: string;\n\tview: string;\n\tattemptId?: string;\n\tcreateDefaultController: () => Promise<RuntimeController> | RuntimeController;\n\tonCompositionChanged?: (composition: unknown) => void;\n}\n\n/**\n * Engine-side `attachHost` args. The host element, framework-error bus,\n * and `sourceCe` together let the adapter dispatch DOM events and bus\n * fan-outs consistently with the kernel emit chain.\n */\nexport interface SectionRuntimeEngineHostArgs {\n\thost: EventTarget;\n\tsourceCe: string;\n\tframeworkErrorBus: FrameworkErrorReporter;\n\tcoordinator?: SectionEngineAdapterOptions[\"coordinator\"];\n\tinstrumentationHook?: InstrumentationHook;\n\tnow?: () => string;\n}\n\nexport class SectionRuntimeEngine {\n\tprivate readonly registry = new RuntimeRegistry();\n\tprivate readonly runtimeId = createRuntimeId(\"section-engine\");\n\n\t// Engine-side state (PR 3+).\n\tprivate adapter: SectionEngineAdapter | null = null;\n\n\t// Controller-side state.\n\tprivate controller: RuntimeController | null = null;\n\tprivate coordinator: ToolkitCoordinator | null = null;\n\tprivate sectionId = \"\";\n\tprivate attemptId: string | undefined;\n\tprivate unsubscribeController: (() => void) | null = null;\n\tprivate activeInitToken = 0;\n\n\t/**\n\t * Tracks which `(canonicalItemId, contentKind)` pairs have already\n\t * fired `handleContentLoaded` on this engine. Mirrored alongside\n\t * `RuntimeRegistry`'s registered set so a cohort handoff can\n\t * replay the persistent shells' loaded state into the new\n\t * controller — see `replayRegisteredShellsIntoController` and the\n\t * PIE-512 Phase B regression test.\n\t *\n\t * Cleared in lockstep with `handleContentUnregistered` and on\n\t * `dispose`. Not exposed publicly: the registry alone is the\n\t * external surface; the loaded set exists only to seed cohort B's\n\t * controller with the live state cohort A's shells produced.\n\t */\n\tprivate readonly loadedRenderableKeys = new Set<string>();\n\n\t// ============================================================\n\t// Engine-side surface (PR 3+; driven by the kernel from PR 5,\n\t// by the toolkit CE from PR 6, and by the facade smoke test today).\n\t// ============================================================\n\n\t/**\n\t * Attach the host element + framework-error bus + sourceCe so the\n\t * adapter can dispatch DOM events and bus errors. Required before\n\t * `dispatchInput` / `subscribe` produce observable effects.\n\t *\n\t * Constructs the adapter on the first call. Subsequent calls keep\n\t * the adapter and update its host element / instrumentation hook\n\t * (used by layout-shell rollovers and instrumentation reconfig).\n\t */\n\tattachHost(args: SectionRuntimeEngineHostArgs): void {\n\t\tif (this.adapter) {\n\t\t\tthis.adapter.setHost(args.host);\n\t\t\tif (args.instrumentationHook !== undefined) {\n\t\t\t\tthis.adapter.setInstrumentationHook(args.instrumentationHook);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tthis.adapter = new SectionEngineAdapter({\n\t\t\thost: args.host,\n\t\t\truntimeId: this.runtimeId,\n\t\t\tsourceCe: args.sourceCe,\n\t\t\tframeworkErrorBus: args.frameworkErrorBus,\n\t\t\tcoordinator: args.coordinator,\n\t\t\tinstrumentationHook: args.instrumentationHook,\n\t\t\tnow: args.now,\n\t\t});\n\t}\n\n\t/**\n\t * Forward a host-constructed input to the FSM core via the adapter.\n\t * Returns the outputs the transition produced (subscribers and the\n\t * DOM/framework-error/instrumentation bridges have already\n\t * received them).\n\t *\n\t * No-op (returns `[]`) before `attachHost(...)` so callers that\n\t * dispatch optimistically during teardown do not throw.\n\t */\n\tdispatchInput(input: SectionEngineInput): readonly SectionEngineOutput[] {\n\t\treturn this.adapter?.dispatchInput(input) ?? [];\n\t}\n\n\t/**\n\t * Subscribe to batched engine outputs (one batch per\n\t * `dispatchInput`). Returns an idempotent disposer. Pre-`attachHost`\n\t * subscriptions return a no-op disposer so callers can wire\n\t * subscriptions early without lifecycle-ordering hazards.\n\t */\n\tsubscribe(listener: EngineOutputListener): () => void {\n\t\treturn this.adapter?.subscribe(listener) ?? noopDispose;\n\t}\n\n\t/**\n\t * Read-only snapshot of the FSM state. Returns the initial idle\n\t * state before `attachHost` so callers can probe state without\n\t * special-casing the pre-attach phase.\n\t */\n\tgetState(): Readonly<SectionEngineState> {\n\t\treturn this.adapter?.getState() ?? createInitialEngineState();\n\t}\n\n\t/**\n\t * Resolve the effective runtime + tools snapshot for the given\n\t * two-tier inputs. This is a pure function over the resolver — it\n\t * does not depend on adapter state — so callers can use it both\n\t * pre- and post-`attachHost`. The kernel uses it to seed\n\t * `dispatchInput({ kind: \"initialize\", effectiveRuntime, ... })`.\n\t */\n\tgetEffectiveRuntime<P>(\n\t\targs: RuntimeInputs,\n\t\tdeps: {\n\t\t\tresolvePlayerRuntime: (resolverArgs: {\n\t\t\t\teffectiveRuntime: Record<string, unknown>;\n\t\t\t\tplayerType: string;\n\t\t\t\tenv: Record<string, unknown> | null;\n\t\t\t}) => P;\n\t\t},\n\t): EffectiveRuntime {\n\t\treturn resolveSectionEngineRuntimeState(args, deps).effectiveRuntime;\n\t}\n\n\t/**\n\t * Update the adapter's instrumentation hook. No-op pre-attach.\n\t * Hosts that always want a hook should pass it via `attachHost`.\n\t */\n\tsetInstrumentationHook(hook: InstrumentationHook | undefined): void {\n\t\tthis.adapter?.setInstrumentationHook(hook);\n\t}\n\n\t/**\n\t * Stable per-engine runtime id used to scope DOM-event details and\n\t * registry telemetry. Exposed so tests and instrumentation can pin\n\t * the value the bridges emit.\n\t */\n\tgetRuntimeId(): string {\n\t\treturn this.runtimeId;\n\t}\n\n\t// ============================================================\n\t// Controller-side surface.\n\t// ============================================================\n\n\tasync initialize(args: EngineInitArgs): Promise<void> {\n\t\tthis.activeInitToken += 1;\n\t\tconst token = this.activeInitToken;\n\t\tthis.coordinator = args.coordinator;\n\t\tthis.sectionId = args.sectionId;\n\t\tthis.attemptId = args.attemptId;\n\n\t\tconst resolved = (await args.coordinator.getOrCreateSectionController({\n\t\t\tsectionId: args.sectionId,\n\t\t\tattemptId: args.attemptId,\n\t\t\tinput: {\n\t\t\t\tsection: args.section,\n\t\t\t\tsectionId: args.sectionId,\n\t\t\t\tassessmentId: args.assessmentId,\n\t\t\t\tview: args.view,\n\t\t\t},\n\t\t\tupdateExisting: true,\n\t\t\tcreateDefaultController: args.createDefaultController,\n\t\t})) as RuntimeController;\n\n\t\tif (token !== this.activeInitToken) return;\n\t\tthis.unsubscribeController?.();\n\t\tthis.controller = resolved;\n\t\t// PIE-512 Phase C: always re-feed the registry's currently\n\t\t// registered shells (and their loaded state) into the resolved\n\t\t// controller. We do NOT gate on `resolved !== previousController`\n\t\t// any more.\n\t\t//\n\t\t// Why the gate had to go:\n\t\t// - Cohort flip resolving to a fresh controller — replay seeds\n\t\t// the new controller, which is the original Phase B fix.\n\t\t// - Same-cohort `updateInput` resolving to the existing\n\t\t// controller — the coordinator's\n\t\t// `resolveExistingSectionController` calls\n\t\t// `existingController.updateInput(input)` (engine always\n\t\t// passes `updateExisting: true`), and pre-Phase-C\n\t\t// `SectionController.initialize` wiped lifecycle tracking\n\t\t// unconditionally. A subscriber attaching between the wipe\n\t\t// and the next live event saw an empty\n\t\t// `loadedRenderables` snapshot.\n\t\t//\n\t\t// Phase C makes this re-feed safe by combining (a) the\n\t\t// section-identity gate around `resetLifecycleTracking()` in\n\t\t// `SectionController.initialize` (so same-cohort `updateInput`\n\t\t// preserves tracking) with (b) idempotent\n\t\t// `handleContentRegistered` / `handleContentLoaded` on the\n\t\t// controller (so re-feeding the same registry entries is a\n\t\t// no-op if the controller already knows about them, but\n\t\t// re-seeds the controller in the post-`updateInput`-wipe case\n\t\t// that pre-Phase-C used to encounter).\n\t\tthis.replayRegisteredShellsIntoController(resolved);\n\t\targs.onCompositionChanged?.(resolved.getCompositionModel?.());\n\t\tthis.unsubscribeController =\n\t\t\tresolved.subscribe?.(() => {\n\t\t\t\targs.onCompositionChanged?.(resolved.getCompositionModel?.());\n\t\t\t}) || null;\n\t}\n\n\t/**\n\t * Re-feed the engine's `RuntimeRegistry` and loaded-set into the\n\t * resolved controller. Call site: `initialize(...)` — runs on\n\t * EVERY initialize, both cohort-flip-resolves-fresh-controller\n\t * and same-cohort-resolves-existing-controller cases. Phase C\n\t * dropped the `resolved !== previousController` gate; see the\n\t * comment at the call site for why.\n\t *\n\t * Two-pass order — register every shell in document order, then\n\t * issue `handleContentLoaded` for each shell whose load already\n\t * fired on this engine. The two-pass shape prevents\n\t * `evaluateSectionLoadingState` from flapping\n\t * `section-loading-complete` `false→true→false→…` while replay is\n\t * mid-walk: any subscriber wired before the engine's own\n\t * `controller.subscribe` (no such caller today, but cheap defense\n\t * for future wiring) sees one clean `false→true` transition.\n\t *\n\t * Idempotent on the controller side — Phase C makes\n\t * `SectionController.handleContentRegistered` and\n\t * `handleContentLoaded` early-return on duplicates so re-feeding\n\t * the same shells into a controller that already tracks them is\n\t * a true no-op (no spurious re-emits, no re-evaluation of\n\t * `section-loading-complete`). At the call site no listener is\n\t * attached during the replay window, so `emitChange` side effects\n\t * fire into a void on this pass anyway, but the controller-side\n\t * idempotence is what makes the replay safe to run on every\n\t * initialize regardless of whether the controller is fresh.\n\t */\n\tprivate replayRegisteredShellsIntoController(\n\t\tcontroller: RuntimeController,\n\t): void {\n\t\tif (!controller.handleContentRegistered) return;\n\t\tconst shells = this.registry.getOrderedShells();\n\t\tif (shells.length === 0) return;\n\t\tfor (const shell of shells) {\n\t\t\tconst canonicalItemId = shell.canonicalItemId || shell.itemId;\n\t\t\tcontroller.handleContentRegistered({\n\t\t\t\titemId: shell.itemId,\n\t\t\t\tcanonicalItemId,\n\t\t\t\tcontentKind: shell.contentKind || shell.kind,\n\t\t\t});\n\t\t}\n\t\tconst now = Date.now();\n\t\tfor (const shell of shells) {\n\t\t\tconst canonicalItemId = shell.canonicalItemId || shell.itemId;\n\t\t\tconst key = this.getLoadedKey(canonicalItemId, shell.contentKind);\n\t\t\tif (this.loadedRenderableKeys.has(key)) {\n\t\t\t\tcontroller.handleContentLoaded?.({\n\t\t\t\t\titemId: shell.itemId,\n\t\t\t\t\tcanonicalItemId,\n\t\t\t\t\tcontentKind: shell.contentKind || shell.kind,\n\t\t\t\t\ttimestamp: now,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Stable key for the engine's `loadedRenderableKeys` set. Mirrors\n\t * the `(canonicalItemId, contentKind)` shape `SectionController`\n\t * uses internally (see `getRenderableKey`); we only need\n\t * consistent add/check semantics on this side, not byte-identical\n\t * normalization with the controller.\n\t */\n\tprivate getLoadedKey(\n\t\tcanonicalItemId: string,\n\t\tcontentKind: string | undefined,\n\t): string {\n\t\treturn `${contentKind ?? \"\"}:${canonicalItemId}`;\n\t}\n\n\tregister(detail: RuntimeRegistrationDetail): boolean {\n\t\treturn this.registry.register(detail);\n\t}\n\n\tunregister(element: HTMLElement): boolean {\n\t\treturn this.registry.unregister(element);\n\t}\n\n\tgetCompositionModel(): unknown {\n\t\treturn this.controller?.getCompositionModel?.() ?? null;\n\t}\n\n\tgetCanonicalItemId(itemId: string): string {\n\t\tconst map = this.registry.getCanonicalIdMap();\n\t\treturn map[itemId] || this.controller?.getCanonicalItemId?.(itemId) || itemId;\n\t}\n\n\thandleContentRegistered(detail: RuntimeRegistrationDetail): void {\n\t\tthis.controller?.handleContentRegistered?.({\n\t\t\titemId: detail.itemId,\n\t\t\tcanonicalItemId: detail.canonicalItemId || detail.itemId,\n\t\t\tcontentKind: detail.contentKind || detail.kind,\n\t\t});\n\t}\n\n\thandleContentUnregistered(detail: RuntimeRegistrationDetail): void {\n\t\tconst canonicalItemId = detail.canonicalItemId || detail.itemId;\n\t\t// Key the load-set delete with the same `contentKind`-only\n\t\t// shape that `handleContentLoaded` and\n\t\t// `replayRegisteredShellsIntoController` use, so add/check/\n\t\t// delete keys round-trip identically. The `|| detail.kind`\n\t\t// fallback is preserved for the controller-forwarded payload\n\t\t// because the controller normalizes via `toSectionContentKind`\n\t\t// and the `kind` field remains meaningful there.\n\t\tthis.loadedRenderableKeys.delete(\n\t\t\tthis.getLoadedKey(canonicalItemId, detail.contentKind),\n\t\t);\n\t\tthis.controller?.handleContentUnregistered?.({\n\t\t\titemId: detail.itemId,\n\t\t\tcanonicalItemId,\n\t\t\tcontentKind: detail.contentKind || detail.kind,\n\t\t});\n\t}\n\n\thandleContentLoaded(args: {\n\t\titemId: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t\tdetail?: unknown;\n\t\ttimestamp?: number;\n\t}): void {\n\t\tconst canonicalItemId = args.canonicalItemId || args.itemId;\n\t\tthis.loadedRenderableKeys.add(\n\t\t\tthis.getLoadedKey(canonicalItemId, args.contentKind),\n\t\t);\n\t\tthis.controller?.handleContentLoaded?.(args);\n\t}\n\n\thandleItemPlayerError(args: {\n\t\titemId: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t\terror: unknown;\n\t\ttimestamp?: number;\n\t}): void {\n\t\tthis.controller?.handleItemPlayerError?.(args);\n\t}\n\n\treportSectionError(args: {\n\t\tsource: \"item-player\" | \"section-runtime\" | \"toolkit\" | \"controller\";\n\t\terror: unknown;\n\t\titemId?: string;\n\t\tcanonicalItemId?: string;\n\t\tcontentKind?: string;\n\t\ttimestamp?: number;\n\t}): void {\n\t\tthis.controller?.reportSectionError?.(args);\n\t}\n\n\tupdateItemSession(itemId: string, session: unknown): unknown {\n\t\tconst canonicalId = this.getCanonicalItemId(itemId);\n\t\treturn this.controller?.updateItemSession?.(canonicalId, session) ?? null;\n\t}\n\n\tnavigateToItem(index: number): unknown {\n\t\treturn this.controller?.navigateToItem?.(index) ?? null;\n\t}\n\n\tasync persist(): Promise<void> {\n\t\tawait this.controller?.persist?.();\n\t}\n\n\tasync hydrate(): Promise<void> {\n\t\tawait this.controller?.hydrate?.();\n\t}\n\n\tgetRegistry(): RuntimeRegistry {\n\t\treturn this.registry;\n\t}\n\n\tasync dispose(): Promise<void> {\n\t\tthis.activeInitToken += 1;\n\t\tthis.unsubscribeController?.();\n\t\tthis.unsubscribeController = null;\n\t\tif (this.coordinator && this.sectionId) {\n\t\t\tawait this.coordinator.disposeSectionController({\n\t\t\t\tsectionId: this.sectionId,\n\t\t\t\tattemptId: this.attemptId,\n\t\t\t});\n\t\t}\n\t\tthis.registry.clear();\n\t\tthis.loadedRenderableKeys.clear();\n\t\tthis.controller = null;\n\t\tthis.coordinator = null;\n\t\t// Tear down the engine-side adapter (if attached) last so any\n\t\t// `dispose` FSM input flows through the bridges before they\n\t\t// detach. Adapter disposal is idempotent and safe even when\n\t\t// nothing is attached.\n\t\tif (this.adapter) {\n\t\t\tconst adapter = this.adapter;\n\t\t\tthis.adapter = null;\n\t\t\tawait adapter.dispose();\n\t\t}\n\t}\n}\n\nconst noopDispose = (): void => {};\n"]}
@@ -29,8 +29,8 @@
29
29
  * - public subscriber fan-out (batched, runs once per
30
30
  * `core.dispatch`)
31
31
  *
32
- * The deprecated `readiness-change` / `interaction-ready` / `ready`
33
- * DOM events and their `legacy-event-bridge.ts` were removed in the
32
+ * The `readiness-change` / `interaction-ready` / `ready`
33
+ * DOM events and their event bridge were removed in the
34
34
  * broad architecture review compat sweep; the corresponding output
35
35
  * kinds are gone from `engine-output.ts`.
36
36
  *
@@ -29,8 +29,8 @@
29
29
  * - public subscriber fan-out (batched, runs once per
30
30
  * `core.dispatch`)
31
31
  *
32
- * The deprecated `readiness-change` / `interaction-ready` / `ready`
33
- * DOM events and their `legacy-event-bridge.ts` were removed in the
32
+ * The `readiness-change` / `interaction-ready` / `ready`
33
+ * DOM events and their event bridge were removed in the
34
34
  * broad architecture review compat sweep; the corresponding output
35
35
  * kinds are gone from `engine-output.ts`.
36
36
  *
@@ -1 +1 @@
1
- {"version":3,"file":"SectionEngineAdapter.js","sourceRoot":"","sources":["../../../src/runtime/adapter/SectionEngineAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAIjE,OAAO,EACN,uBAAuB,GAIvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACN,oBAAoB,GAEpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,0BAA0B,GAE1B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACN,2BAA2B,GAG3B,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACN,sBAAsB,GAGtB,MAAM,wBAAwB,CAAC;AAqChC,MAAM,OAAO,oBAAoB;IACf,IAAI,CAAoB;IACxB,cAAc,CAAuB;IACrC,oBAAoB,CAA6B;IACjD,qBAAqB,CAA8B;IACnD,gBAAgB,CAAyB;IACzC,iBAAiB,CAAiC;IAClD,eAAe,CAAa;IACrC,QAAQ,GAAG,KAAK,CAAC;IAEzB,YAAY,OAAoC;QAC/C,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,iBAAiB,EAAE,CAAC;QACpD,IAAI,CAAC,cAAc,GAAG,oBAAoB,CAAC;YAC1C,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,GAAG,EAAE,OAAO,CAAC,GAAG;SAChB,CAAC,CAAC;QACH,IAAI,CAAC,oBAAoB,GAAG,0BAA0B,CAAC;YACtD,GAAG,EAAE,OAAO,CAAC,iBAAiB;SAC9B,CAAC,CAAC;QACH,IAAI,CAAC,qBAAqB,GAAG,2BAA2B,CAAC;YACxD,IAAI,EAAE,OAAO,CAAC,mBAAmB;SACjC,CAAC,CAAC;QACH,IAAI,CAAC,gBAAgB,GAAG,sBAAsB,EAAE,CAAC;QACjD,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,WAAW;YAC3C,CAAC,CAAC,uBAAuB,CAAC;gBACxB,WAAW,EAAE,OAAO,CAAC,WAAW;gBAChC,iBAAiB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;aACvD,CAAC;YACH,CAAC,CAAC,IAAI,CAAC;QAER,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE;YACtD,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,QAAQ;QACP,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC7B,CAAC;IAED;;;OAGG;IACH,aAAa,CAAC,KAAyB;QACtC,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClC,CAAC;IAED;;;OAGG;IACH,SAAS,CAAC,QAA8B;QACvC,OAAO,IAAI,CAAC,gBAAgB,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAClD,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,IAAiB;QACxB,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED;;OAEG;IACH,sBAAsB,CAAC,IAAqC;QAC3D,IAAI,CAAC,qBAAqB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED;;;;;;;OAOG;IACH,wBAAwB,CAAC,IAAkC;QAC1D,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CACd,+EAA+E,CAC/E,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC,iBAAiB,CAAC,wBAAwB,CAAC,IAAI,CAAC,CAAC;IAC9D,CAAC;IAED;;;;;;;;OAQG;IACH,KAAK,CAAC,OAAO;QACZ,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QACrB,+DAA+D;QAC/D,iEAAiE;QACjE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;QAChC,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC5B,MAAM,IAAI,CAAC,iBAAiB,CAAC,OAAO,EAAE,CAAC;QACxC,CAAC;IACF,CAAC;IAEO,YAAY,CAAC,OAAuC;QAC3D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC9B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAEO,QAAQ,CAAC,MAA2B;QAC3C,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACrB,KAAK,cAAc,CAAC;YACpB,KAAK,kBAAkB;gBACtB,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACrC,MAAM;YACP,KAAK,iBAAiB;gBACrB,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACrC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBAC3C,MAAM;YACP,OAAO,CAAC,CAAC,CAAC;gBACT,MAAM,UAAU,GAAU,MAAM,CAAC;gBACjC,KAAK,UAAU,CAAC;gBAChB,OAAO;YACR,CAAC;QACF,CAAC;QACD,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;CACD","sourcesContent":["/**\n * Section runtime engine adapter (M7 — Variant C, layered).\n *\n * The adapter is the single I/O seam between the pure FSM core\n * (`SectionEngineCore`) and the rest of the world (DOM, coordinator,\n * framework-error bus, host subscribers, instrumentation). It does\n * exactly two jobs:\n *\n * 1. Translate **host inputs** (from kernels, the toolkit CE, or\n * direct facade callers) into `SectionEngineInput`s and forward\n * them to the core via `core.dispatch(input)`.\n *\n * 2. Subscribe to the core's output stream and **route each output**\n * through the bridges in a deterministic order so the public DOM\n * surface, framework-error bus, instrumentation hook, and host\n * subscribers all see exactly the same fan-out with\n * byte-identical detail shapes.\n *\n * Output routing is a single exhaustive `switch` (`assertNever` on\n * the default branch) so adding a new `SectionEngineOutput` kind in\n * `engine-output.ts` is a compile error here until the adapter knows\n * what to do with it.\n *\n * **Routing order per output:**\n * - canonical DOM event (`pie-stage-change`, `pie-loading-complete`,\n * `framework-error`)\n * - framework-error bus (single fan-out for in-process subscribers)\n * - instrumentation hook\n * - public subscriber fan-out (batched, runs once per\n * `core.dispatch`)\n *\n * The deprecated `readiness-change` / `interaction-ready` / `ready`\n * DOM events and their `legacy-event-bridge.ts` were removed in the\n * broad architecture review compat sweep; the corresponding output\n * kinds are gone from `engine-output.ts`.\n *\n * **Layering constraint.** The adapter is plain TS. It must not\n * import `svelte` — the M7 implementation plan and\n * `scripts/check-engine-core-purity.mjs` enforce the core's purity;\n * the adapter follows the same constraint by convention so it stays\n * usable from non-Svelte hosts (Node tests, Storybook, or future\n * non-Svelte consumers). `bun run check:custom-elements` already\n * blocks `.svelte` imports in published `dist`; adding a Svelte\n * dependency here would also leak through that gate.\n */\n\nimport type { FrameworkErrorReporter } from \"../../services/framework-error-bus.js\";\nimport { SectionEngineCore } from \"../core/SectionEngineCore.js\";\nimport type { SectionEngineInput } from \"../core/engine-input.js\";\nimport type { SectionEngineOutput } from \"../core/engine-output.js\";\nimport type { SectionEngineState } from \"../core/engine-state.js\";\nimport {\n\tcreateCoordinatorBridge,\n\ttype CoordinatorBridgeHandle,\n\ttype CoordinatorPort,\n\ttype ResolveSectionControllerArgs,\n} from \"./coordinator-bridge.js\";\nimport {\n\tcreateDomEventBridge,\n\ttype DomEventBridgeHandle,\n} from \"./dom-event-bridge.js\";\nimport {\n\tcreateFrameworkErrorBridge,\n\ttype FrameworkErrorBridgeHandle,\n} from \"./framework-error-bridge.js\";\nimport {\n\tcreateInstrumentationBridge,\n\ttype InstrumentationBridgeHandle,\n\ttype InstrumentationHook,\n} from \"./instrumentation-bridge.js\";\nimport {\n\tcreateSubscriberFanout,\n\ttype EngineOutputListener,\n\ttype SubscriberFanoutHandle,\n} from \"./subscriber-fanout.js\";\n\nexport interface SectionEngineAdapterOptions {\n\t/** Element on which DOM events are dispatched. */\n\thost: EventTarget;\n\t/** Stable runtime id for this engine instance. */\n\truntimeId: string;\n\t/**\n\t * Tag name of the host CE without the `--version-<encoded>` suffix.\n\t */\n\tsourceCe: string;\n\t/**\n\t * Framework-error bus (write side). Shared with the toolkit CE so\n\t * subscribers via `coordinator.subscribeFrameworkErrors` see the\n\t * same fan-out.\n\t */\n\tframeworkErrorBus: FrameworkErrorReporter;\n\t/**\n\t * Optional toolkit coordinator. When supplied, the adapter wires\n\t * its framework-error subscription, controller resolution, and\n\t * disposal through the coordinator-bridge. Standalone callers (no\n\t * coordinator) can omit this and feed `framework-error` and\n\t * `section-controller-resolved` inputs directly.\n\t */\n\tcoordinator?: CoordinatorPort;\n\t/** Optional one-shot observability hook. */\n\tinstrumentationHook?: InstrumentationHook;\n\t/** Clock injection for the DOM bridge. Defaults to `Date.now()`. */\n\tnow?: () => string;\n\t/**\n\t * Optional pre-constructed core. Defaults to a fresh\n\t * `SectionEngineCore`. Tests pass a mocked core to assert dispatch\n\t * ordering; production callers use the default.\n\t */\n\tcore?: SectionEngineCore;\n}\n\nexport class SectionEngineAdapter {\n\tprivate readonly core: SectionEngineCore;\n\tprivate readonly domEventBridge: DomEventBridgeHandle;\n\tprivate readonly frameworkErrorBridge: FrameworkErrorBridgeHandle;\n\tprivate readonly instrumentationBridge: InstrumentationBridgeHandle;\n\tprivate readonly subscriberFanout: SubscriberFanoutHandle;\n\tprivate readonly coordinatorBridge: CoordinatorBridgeHandle | null;\n\tprivate readonly unsubscribeCore: () => void;\n\tprivate disposed = false;\n\n\tconstructor(options: SectionEngineAdapterOptions) {\n\t\tthis.core = options.core ?? new SectionEngineCore();\n\t\tthis.domEventBridge = createDomEventBridge({\n\t\t\thost: options.host,\n\t\t\truntimeId: options.runtimeId,\n\t\t\tsourceCe: options.sourceCe,\n\t\t\tnow: options.now,\n\t\t});\n\t\tthis.frameworkErrorBridge = createFrameworkErrorBridge({\n\t\t\tbus: options.frameworkErrorBus,\n\t\t});\n\t\tthis.instrumentationBridge = createInstrumentationBridge({\n\t\t\thook: options.instrumentationHook,\n\t\t});\n\t\tthis.subscriberFanout = createSubscriberFanout();\n\t\tthis.coordinatorBridge = options.coordinator\n\t\t\t? createCoordinatorBridge({\n\t\t\t\t\tcoordinator: options.coordinator,\n\t\t\t\t\tdispatchCoreInput: (input) => this.dispatchInput(input),\n\t\t\t\t})\n\t\t\t: null;\n\n\t\tthis.unsubscribeCore = this.core.subscribe((outputs) => {\n\t\t\tthis.routeOutputs(outputs);\n\t\t});\n\t}\n\n\t/**\n\t * Read-only snapshot of the FSM state. Mirrors\n\t * `SectionEngineCore.getState()`.\n\t */\n\tgetState(): Readonly<SectionEngineState> {\n\t\treturn this.core.getState();\n\t}\n\n\t/**\n\t * Forward a host-constructed input to the core. Returns the outputs\n\t * that resulted (the bridges have already received them).\n\t */\n\tdispatchInput(input: SectionEngineInput): readonly SectionEngineOutput[] {\n\t\tif (this.disposed) return [];\n\t\treturn this.core.dispatch(input);\n\t}\n\n\t/**\n\t * Subscribe to batched output streams, one batch per\n\t * `dispatchInput`.\n\t */\n\tsubscribe(listener: EngineOutputListener): () => void {\n\t\treturn this.subscriberFanout.subscribe(listener);\n\t}\n\n\t/**\n\t * Update the host element after construction (e.g. when the kernel\n\t * receives its host element via Svelte action / context).\n\t */\n\tsetHost(host: EventTarget): void {\n\t\tthis.domEventBridge.setHost(host);\n\t}\n\n\t/**\n\t * Update the instrumentation hook at runtime.\n\t */\n\tsetInstrumentationHook(hook: InstrumentationHook | undefined): void {\n\t\tthis.instrumentationBridge.setHook(hook);\n\t}\n\n\t/**\n\t * Resolve a section controller through the configured coordinator.\n\t *\n\t * Throws if the adapter was constructed without a coordinator.\n\t * Standalone callers should manually feed\n\t * `{ kind: \"section-controller-resolved\" }` after their own\n\t * controller resolution path completes.\n\t */\n\tresolveSectionController(args: ResolveSectionControllerArgs): Promise<void> {\n\t\tif (!this.coordinatorBridge) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[SectionEngineAdapter] resolveSectionController called without a coordinator.\",\n\t\t\t);\n\t\t}\n\t\treturn this.coordinatorBridge.resolveSectionController(args);\n\t}\n\n\t/**\n\t * Tear the adapter down. Safe to call more than once. Disposes the\n\t * coordinator bridge (which in turn disposes the section\n\t * controller for the current cohort), detaches the core\n\t * subscription, and clears subscribers.\n\t *\n\t * Does **not** dispose the framework-error bus — its lifetime is\n\t * owned by the toolkit CE / host.\n\t */\n\tasync dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\t// Dispatch the FSM `dispose` input first so the disposed-stage\n\t\t// output flows through the bridges before we tear anything down.\n\t\tthis.core.dispatch({ kind: \"dispose\" });\n\t\tthis.unsubscribeCore();\n\t\tthis.subscriberFanout.dispose();\n\t\tif (this.coordinatorBridge) {\n\t\t\tawait this.coordinatorBridge.dispose();\n\t\t}\n\t}\n\n\tprivate routeOutputs(outputs: readonly SectionEngineOutput[]): void {\n\t\tfor (const output of outputs) {\n\t\t\tthis.routeOne(output);\n\t\t}\n\t\tthis.subscriberFanout.emit(outputs);\n\t}\n\n\tprivate routeOne(output: SectionEngineOutput): void {\n\t\tswitch (output.kind) {\n\t\t\tcase \"stage-change\":\n\t\t\tcase \"loading-complete\":\n\t\t\t\tthis.domEventBridge.dispatch(output);\n\t\t\t\tbreak;\n\t\t\tcase \"framework-error\":\n\t\t\t\tthis.domEventBridge.dispatch(output);\n\t\t\t\tthis.frameworkErrorBridge.dispatch(output);\n\t\t\t\tbreak;\n\t\t\tdefault: {\n\t\t\t\tconst exhaustive: never = output;\n\t\t\t\tvoid exhaustive;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tthis.instrumentationBridge.dispatch(output);\n\t}\n}\n"]}
1
+ {"version":3,"file":"SectionEngineAdapter.js","sourceRoot":"","sources":["../../../src/runtime/adapter/SectionEngineAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Section runtime engine adapter (M7 — Variant C, layered).\n *\n * The adapter is the single I/O seam between the pure FSM core\n * (`SectionEngineCore`) and the rest of the world (DOM, coordinator,\n * framework-error bus, host subscribers, instrumentation). It does\n * exactly two jobs:\n *\n * 1. Translate **host inputs** (from kernels, the toolkit CE, or\n * direct facade callers) into `SectionEngineInput`s and forward\n * them to the core via `core.dispatch(input)`.\n *\n * 2. Subscribe to the core's output stream and **route each output**\n * through the bridges in a deterministic order so the public DOM\n * surface, framework-error bus, instrumentation hook, and host\n * subscribers all see exactly the same fan-out with\n * byte-identical detail shapes.\n *\n * Output routing is a single exhaustive `switch` (`assertNever` on\n * the default branch) so adding a new `SectionEngineOutput` kind in\n * `engine-output.ts` is a compile error here until the adapter knows\n * what to do with it.\n *\n * **Routing order per output:**\n * - canonical DOM event (`pie-stage-change`, `pie-loading-complete`,\n * `framework-error`)\n * - framework-error bus (single fan-out for in-process subscribers)\n * - instrumentation hook\n * - public subscriber fan-out (batched, runs once per\n * `core.dispatch`)\n *\n * The `readiness-change` / `interaction-ready` / `ready`\n * DOM events and their event bridge were removed in the\n * broad architecture review compat sweep; the corresponding output\n * kinds are gone from `engine-output.ts`.\n *\n * **Layering constraint.** The adapter is plain TS. It must not\n * import `svelte` — the M7 implementation plan and\n * `scripts/check-engine-core-purity.mjs` enforce the core's purity;\n * the adapter follows the same constraint by convention so it stays\n * usable from non-Svelte hosts (Node tests, Storybook, or future\n * non-Svelte consumers). `bun run check:custom-elements` already\n * blocks `.svelte` imports in published `dist`; adding a Svelte\n * dependency here would also leak through that gate.\n */\n\nimport type { FrameworkErrorReporter } from \"../../services/framework-error-bus.js\";\nimport { SectionEngineCore } from \"../core/SectionEngineCore.js\";\nimport type { SectionEngineInput } from \"../core/engine-input.js\";\nimport type { SectionEngineOutput } from \"../core/engine-output.js\";\nimport type { SectionEngineState } from \"../core/engine-state.js\";\nimport {\n\tcreateCoordinatorBridge,\n\ttype CoordinatorBridgeHandle,\n\ttype CoordinatorPort,\n\ttype ResolveSectionControllerArgs,\n} from \"./coordinator-bridge.js\";\nimport {\n\tcreateDomEventBridge,\n\ttype DomEventBridgeHandle,\n} from \"./dom-event-bridge.js\";\nimport {\n\tcreateFrameworkErrorBridge,\n\ttype FrameworkErrorBridgeHandle,\n} from \"./framework-error-bridge.js\";\nimport {\n\tcreateInstrumentationBridge,\n\ttype InstrumentationBridgeHandle,\n\ttype InstrumentationHook,\n} from \"./instrumentation-bridge.js\";\nimport {\n\tcreateSubscriberFanout,\n\ttype EngineOutputListener,\n\ttype SubscriberFanoutHandle,\n} from \"./subscriber-fanout.js\";\n\nexport interface SectionEngineAdapterOptions {\n\t/** Element on which DOM events are dispatched. */\n\thost: EventTarget;\n\t/** Stable runtime id for this engine instance. */\n\truntimeId: string;\n\t/**\n\t * Tag name of the host CE without the `--version-<encoded>` suffix.\n\t */\n\tsourceCe: string;\n\t/**\n\t * Framework-error bus (write side). Shared with the toolkit CE so\n\t * subscribers via `coordinator.subscribeFrameworkErrors` see the\n\t * same fan-out.\n\t */\n\tframeworkErrorBus: FrameworkErrorReporter;\n\t/**\n\t * Optional toolkit coordinator. When supplied, the adapter wires\n\t * its framework-error subscription, controller resolution, and\n\t * disposal through the coordinator-bridge. Standalone callers (no\n\t * coordinator) can omit this and feed `framework-error` and\n\t * `section-controller-resolved` inputs directly.\n\t */\n\tcoordinator?: CoordinatorPort;\n\t/** Optional one-shot observability hook. */\n\tinstrumentationHook?: InstrumentationHook;\n\t/** Clock injection for the DOM bridge. Defaults to `Date.now()`. */\n\tnow?: () => string;\n\t/**\n\t * Optional pre-constructed core. Defaults to a fresh\n\t * `SectionEngineCore`. Tests pass a mocked core to assert dispatch\n\t * ordering; production callers use the default.\n\t */\n\tcore?: SectionEngineCore;\n}\n\nexport class SectionEngineAdapter {\n\tprivate readonly core: SectionEngineCore;\n\tprivate readonly domEventBridge: DomEventBridgeHandle;\n\tprivate readonly frameworkErrorBridge: FrameworkErrorBridgeHandle;\n\tprivate readonly instrumentationBridge: InstrumentationBridgeHandle;\n\tprivate readonly subscriberFanout: SubscriberFanoutHandle;\n\tprivate readonly coordinatorBridge: CoordinatorBridgeHandle | null;\n\tprivate readonly unsubscribeCore: () => void;\n\tprivate disposed = false;\n\n\tconstructor(options: SectionEngineAdapterOptions) {\n\t\tthis.core = options.core ?? new SectionEngineCore();\n\t\tthis.domEventBridge = createDomEventBridge({\n\t\t\thost: options.host,\n\t\t\truntimeId: options.runtimeId,\n\t\t\tsourceCe: options.sourceCe,\n\t\t\tnow: options.now,\n\t\t});\n\t\tthis.frameworkErrorBridge = createFrameworkErrorBridge({\n\t\t\tbus: options.frameworkErrorBus,\n\t\t});\n\t\tthis.instrumentationBridge = createInstrumentationBridge({\n\t\t\thook: options.instrumentationHook,\n\t\t});\n\t\tthis.subscriberFanout = createSubscriberFanout();\n\t\tthis.coordinatorBridge = options.coordinator\n\t\t\t? createCoordinatorBridge({\n\t\t\t\t\tcoordinator: options.coordinator,\n\t\t\t\t\tdispatchCoreInput: (input) => this.dispatchInput(input),\n\t\t\t\t})\n\t\t\t: null;\n\n\t\tthis.unsubscribeCore = this.core.subscribe((outputs) => {\n\t\t\tthis.routeOutputs(outputs);\n\t\t});\n\t}\n\n\t/**\n\t * Read-only snapshot of the FSM state. Mirrors\n\t * `SectionEngineCore.getState()`.\n\t */\n\tgetState(): Readonly<SectionEngineState> {\n\t\treturn this.core.getState();\n\t}\n\n\t/**\n\t * Forward a host-constructed input to the core. Returns the outputs\n\t * that resulted (the bridges have already received them).\n\t */\n\tdispatchInput(input: SectionEngineInput): readonly SectionEngineOutput[] {\n\t\tif (this.disposed) return [];\n\t\treturn this.core.dispatch(input);\n\t}\n\n\t/**\n\t * Subscribe to batched output streams, one batch per\n\t * `dispatchInput`.\n\t */\n\tsubscribe(listener: EngineOutputListener): () => void {\n\t\treturn this.subscriberFanout.subscribe(listener);\n\t}\n\n\t/**\n\t * Update the host element after construction (e.g. when the kernel\n\t * receives its host element via Svelte action / context).\n\t */\n\tsetHost(host: EventTarget): void {\n\t\tthis.domEventBridge.setHost(host);\n\t}\n\n\t/**\n\t * Update the instrumentation hook at runtime.\n\t */\n\tsetInstrumentationHook(hook: InstrumentationHook | undefined): void {\n\t\tthis.instrumentationBridge.setHook(hook);\n\t}\n\n\t/**\n\t * Resolve a section controller through the configured coordinator.\n\t *\n\t * Throws if the adapter was constructed without a coordinator.\n\t * Standalone callers should manually feed\n\t * `{ kind: \"section-controller-resolved\" }` after their own\n\t * controller resolution path completes.\n\t */\n\tresolveSectionController(args: ResolveSectionControllerArgs): Promise<void> {\n\t\tif (!this.coordinatorBridge) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[SectionEngineAdapter] resolveSectionController called without a coordinator.\",\n\t\t\t);\n\t\t}\n\t\treturn this.coordinatorBridge.resolveSectionController(args);\n\t}\n\n\t/**\n\t * Tear the adapter down. Safe to call more than once. Disposes the\n\t * coordinator bridge (which in turn disposes the section\n\t * controller for the current cohort), detaches the core\n\t * subscription, and clears subscribers.\n\t *\n\t * Does **not** dispose the framework-error bus — its lifetime is\n\t * owned by the toolkit CE / host.\n\t */\n\tasync dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\t// Dispatch the FSM `dispose` input first so the disposed-stage\n\t\t// output flows through the bridges before we tear anything down.\n\t\tthis.core.dispatch({ kind: \"dispose\" });\n\t\tthis.unsubscribeCore();\n\t\tthis.subscriberFanout.dispose();\n\t\tif (this.coordinatorBridge) {\n\t\t\tawait this.coordinatorBridge.dispose();\n\t\t}\n\t}\n\n\tprivate routeOutputs(outputs: readonly SectionEngineOutput[]): void {\n\t\tfor (const output of outputs) {\n\t\t\tthis.routeOne(output);\n\t\t}\n\t\tthis.subscriberFanout.emit(outputs);\n\t}\n\n\tprivate routeOne(output: SectionEngineOutput): void {\n\t\tswitch (output.kind) {\n\t\t\tcase \"stage-change\":\n\t\t\tcase \"loading-complete\":\n\t\t\t\tthis.domEventBridge.dispatch(output);\n\t\t\t\tbreak;\n\t\t\tcase \"framework-error\":\n\t\t\t\tthis.domEventBridge.dispatch(output);\n\t\t\t\tthis.frameworkErrorBridge.dispatch(output);\n\t\t\t\tbreak;\n\t\t\tdefault: {\n\t\t\t\tconst exhaustive: never = output;\n\t\t\t\tvoid exhaustive;\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tthis.instrumentationBridge.dispatch(output);\n\t}\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  * Coordinator bridge for the section runtime engine adapter (M7 —
3
3
  * Variant C, layered).
4
4
  *
5
- * Unlike the DOM / legacy / framework-error bridges (which translate
5
+ * Unlike the DOM / framework-error bridges (which translate
6
6
  * **outputs** into I/O), the coordinator bridge is an **input source**:
7
7
  * it listens to a `ToolkitCoordinator` and feeds the resulting events
8
8
  * into the core via the `dispatchCoreInput` callback supplied by the
@@ -2,7 +2,7 @@
2
2
  * Coordinator bridge for the section runtime engine adapter (M7 —
3
3
  * Variant C, layered).
4
4
  *
5
- * Unlike the DOM / legacy / framework-error bridges (which translate
5
+ * Unlike the DOM / framework-error bridges (which translate
6
6
  * **outputs** into I/O), the coordinator bridge is an **input source**:
7
7
  * it listens to a `ToolkitCoordinator` and feeds the resulting events
8
8
  * into the core via the `dispatchCoreInput` callback supplied by the
@@ -1 +1 @@
1
- {"version":3,"file":"coordinator-bridge.js","sourceRoot":"","sources":["../../../src/runtime/adapter/coordinator-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Coordinator bridge for the section runtime engine adapter (M7 —\n * Variant C, layered).\n *\n * Unlike the DOM / legacy / framework-error bridges (which translate\n * **outputs** into I/O), the coordinator bridge is an **input source**:\n * it listens to a `ToolkitCoordinator` and feeds the resulting events\n * into the core via the `dispatchCoreInput` callback supplied by the\n * adapter.\n *\n * Responsibilities:\n * 1. **Framework-error subscription.** When the coordinator reports\n * a framework error through its bus, dispatch a\n * `{ kind: \"framework-error\" }` core input. Listener isolation\n * matches `FrameworkErrorBus`: a throwing bridge handler is\n * caught and `console.warn`-logged so the original fan-out is\n * not interrupted.\n *\n * 2. **Section-controller resolution.** When the host calls\n * `resolveSectionController({ ... })`, the bridge invokes\n * `coordinator.getOrCreateSectionController({ ... })` and, on\n * successful resolution, dispatches\n * `{ kind: \"section-controller-resolved\" }` to the core. Init\n * tokens guard against late resolutions overwriting a more\n * recent cohort (mirrors `SectionRuntimeEngine.initialize`'s\n * `activeInitToken` pattern).\n *\n * 3. **Disposal.** On `dispose()`, the bridge unsubscribes from the\n * framework-error bus and (if the engine owns the section\n * controller for the cohort) calls\n * `coordinator.disposeSectionController({ ... })`. It does\n * **not** dispose the coordinator itself — the coordinator's\n * lifetime is owned by the toolkit CE / host, not by the engine.\n *\n * Disposal is idempotent: calling `dispose()` twice is a no-op.\n *\n * The bridge **must not** import `svelte`.\n */\n\nimport type {\n\tSectionControllerEvent,\n\tSectionControllerHandle,\n} from \"../../services/section-controller-types.js\";\nimport type { CohortKey } from \"../core/cohort.js\";\nimport type { SectionEngineInput } from \"../core/engine-input.js\";\n\n/**\n * Subset of the `ToolkitCoordinator` surface the bridge consumes. Kept\n * structural so tests can pass a fake without pulling the full\n * coordinator.\n */\nexport interface CoordinatorPort {\n\tsubscribeFrameworkErrors(\n\t\tlistener: (\n\t\t\tmodel: import(\"../../services/framework-error.js\").FrameworkErrorModel,\n\t\t) => void,\n\t): () => void;\n\n\tgetOrCreateSectionController(args: {\n\t\tsectionId: string;\n\t\tattemptId?: string;\n\t\tinput?: unknown;\n\t\tupdateExisting?: boolean;\n\t\tcreateDefaultController: () =>\n\t\t\t| SectionControllerHandle\n\t\t\t| Promise<SectionControllerHandle>;\n\t}): Promise<SectionControllerHandle>;\n\n\tdisposeSectionController(args: {\n\t\tsectionId: string;\n\t\tattemptId?: string;\n\t}): Promise<void>;\n}\n\nexport interface ResolveSectionControllerArgs {\n\tcohort: CohortKey;\n\tassessmentId: string;\n\tview: string;\n\tsection: unknown;\n\tcreateDefaultController: () =>\n\t\t| SectionControllerHandle\n\t\t| Promise<SectionControllerHandle>;\n\t/**\n\t * Optional callback invoked when the controller resolves and on\n\t * every controller `subscribe` event. The kernel uses this to keep\n\t * its composition snapshot in sync with the controller's view.\n\t *\n\t * The bridge is responsible for unsubscribing on dispose / cohort\n\t * change so a stale controller doesn't keep emitting after rollover.\n\t */\n\tonCompositionChanged?: (composition: unknown) => void;\n\t/**\n\t * Optional callback invoked once with the resolved controller handle\n\t * the moment it is available. Lets the host keep a reference for\n\t * direct method calls (e.g. `navigateToItem`, `applySession`)\n\t * without re-querying the coordinator.\n\t */\n\tonControllerResolved?: (controller: SectionControllerHandle) => void;\n}\n\nexport interface CoordinatorBridgeOptions {\n\tcoordinator: CoordinatorPort;\n\tdispatchCoreInput: (input: SectionEngineInput) => void;\n}\n\nexport interface CoordinatorBridgeHandle {\n\tresolveSectionController(args: ResolveSectionControllerArgs): Promise<void>;\n\tdispose(): Promise<void>;\n}\n\nexport function createCoordinatorBridge(\n\toptions: CoordinatorBridgeOptions,\n): CoordinatorBridgeHandle {\n\tconst { coordinator, dispatchCoreInput } = options;\n\n\tlet activeInitToken = 0;\n\tlet activeCohort: CohortKey | null = null;\n\tlet unsubscribeController: (() => void) | null = null;\n\tlet disposed = false;\n\n\tconst unsubscribeFrameworkErrors = coordinator.subscribeFrameworkErrors(\n\t\t(model) => {\n\t\t\ttry {\n\t\t\t\tdispatchCoreInput({ kind: \"framework-error\", error: model });\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"[CoordinatorBridge] framework-error dispatch failed:\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n\n\tfunction detachControllerSubscription(): void {\n\t\tif (!unsubscribeController) return;\n\t\ttry {\n\t\t\tunsubscribeController();\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[CoordinatorBridge] controller unsubscribe failed:\", error);\n\t\t}\n\t\tunsubscribeController = null;\n\t}\n\n\tasync function resolveSectionController(\n\t\targs: ResolveSectionControllerArgs,\n\t): Promise<void> {\n\t\tif (disposed) return;\n\t\tactiveInitToken += 1;\n\t\tconst token = activeInitToken;\n\t\tactiveCohort = args.cohort;\n\n\t\tconst controller = await coordinator.getOrCreateSectionController({\n\t\t\tsectionId: args.cohort.sectionId,\n\t\t\tattemptId: args.cohort.attemptId || undefined,\n\t\t\tinput: {\n\t\t\t\tsection: args.section,\n\t\t\t\tsectionId: args.cohort.sectionId,\n\t\t\t\tassessmentId: args.assessmentId,\n\t\t\t\tview: args.view,\n\t\t\t},\n\t\t\tupdateExisting: true,\n\t\t\tcreateDefaultController: args.createDefaultController,\n\t\t});\n\n\t\t// Drop late resolutions — a cohort change or dispose may have\n\t\t// rolled the token forward while we awaited the coordinator.\n\t\tif (token !== activeInitToken || disposed) return;\n\n\t\tdetachControllerSubscription();\n\n\t\targs.onControllerResolved?.(controller);\n\n\t\tconst getCompositionModel = (\n\t\t\tcontroller as SectionControllerHandle & {\n\t\t\t\tgetCompositionModel?: () => unknown;\n\t\t\t}\n\t\t).getCompositionModel;\n\t\targs.onCompositionChanged?.(getCompositionModel?.());\n\n\t\tif (typeof controller.subscribe === \"function\") {\n\t\t\tunsubscribeController = controller.subscribe(\n\t\t\t\t(_event: SectionControllerEvent) => {\n\t\t\t\t\tif (token !== activeInitToken || disposed) return;\n\t\t\t\t\targs.onCompositionChanged?.(getCompositionModel?.());\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tdispatchCoreInput({ kind: \"section-controller-resolved\" });\n\t}\n\n\tasync function dispose(): Promise<void> {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tactiveInitToken += 1;\n\t\tdetachControllerSubscription();\n\t\ttry {\n\t\t\tunsubscribeFrameworkErrors();\n\t\t} catch (error) {\n\t\t\tconsole.warn(\n\t\t\t\t\"[CoordinatorBridge] framework-error unsubscribe failed:\",\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\t\tconst cohort = activeCohort;\n\t\tactiveCohort = null;\n\t\tif (cohort) {\n\t\t\ttry {\n\t\t\t\tawait coordinator.disposeSectionController({\n\t\t\t\t\tsectionId: cohort.sectionId,\n\t\t\t\t\tattemptId: cohort.attemptId || undefined,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"[CoordinatorBridge] section-controller dispose failed:\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { resolveSectionController, dispose };\n}\n"]}
1
+ {"version":3,"file":"coordinator-bridge.js","sourceRoot":"","sources":["../../../src/runtime/adapter/coordinator-bridge.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Coordinator bridge for the section runtime engine adapter (M7 —\n * Variant C, layered).\n *\n * Unlike the DOM / framework-error bridges (which translate\n * **outputs** into I/O), the coordinator bridge is an **input source**:\n * it listens to a `ToolkitCoordinator` and feeds the resulting events\n * into the core via the `dispatchCoreInput` callback supplied by the\n * adapter.\n *\n * Responsibilities:\n * 1. **Framework-error subscription.** When the coordinator reports\n * a framework error through its bus, dispatch a\n * `{ kind: \"framework-error\" }` core input. Listener isolation\n * matches `FrameworkErrorBus`: a throwing bridge handler is\n * caught and `console.warn`-logged so the original fan-out is\n * not interrupted.\n *\n * 2. **Section-controller resolution.** When the host calls\n * `resolveSectionController({ ... })`, the bridge invokes\n * `coordinator.getOrCreateSectionController({ ... })` and, on\n * successful resolution, dispatches\n * `{ kind: \"section-controller-resolved\" }` to the core. Init\n * tokens guard against late resolutions overwriting a more\n * recent cohort (mirrors `SectionRuntimeEngine.initialize`'s\n * `activeInitToken` pattern).\n *\n * 3. **Disposal.** On `dispose()`, the bridge unsubscribes from the\n * framework-error bus and (if the engine owns the section\n * controller for the cohort) calls\n * `coordinator.disposeSectionController({ ... })`. It does\n * **not** dispose the coordinator itself — the coordinator's\n * lifetime is owned by the toolkit CE / host, not by the engine.\n *\n * Disposal is idempotent: calling `dispose()` twice is a no-op.\n *\n * The bridge **must not** import `svelte`.\n */\n\nimport type {\n\tSectionControllerEvent,\n\tSectionControllerHandle,\n} from \"../../services/section-controller-types.js\";\nimport type { CohortKey } from \"../core/cohort.js\";\nimport type { SectionEngineInput } from \"../core/engine-input.js\";\n\n/**\n * Subset of the `ToolkitCoordinator` surface the bridge consumes. Kept\n * structural so tests can pass a fake without pulling the full\n * coordinator.\n */\nexport interface CoordinatorPort {\n\tsubscribeFrameworkErrors(\n\t\tlistener: (\n\t\t\tmodel: import(\"../../services/framework-error.js\").FrameworkErrorModel,\n\t\t) => void,\n\t): () => void;\n\n\tgetOrCreateSectionController(args: {\n\t\tsectionId: string;\n\t\tattemptId?: string;\n\t\tinput?: unknown;\n\t\tupdateExisting?: boolean;\n\t\tcreateDefaultController: () =>\n\t\t\t| SectionControllerHandle\n\t\t\t| Promise<SectionControllerHandle>;\n\t}): Promise<SectionControllerHandle>;\n\n\tdisposeSectionController(args: {\n\t\tsectionId: string;\n\t\tattemptId?: string;\n\t}): Promise<void>;\n}\n\nexport interface ResolveSectionControllerArgs {\n\tcohort: CohortKey;\n\tassessmentId: string;\n\tview: string;\n\tsection: unknown;\n\tcreateDefaultController: () =>\n\t\t| SectionControllerHandle\n\t\t| Promise<SectionControllerHandle>;\n\t/**\n\t * Optional callback invoked when the controller resolves and on\n\t * every controller `subscribe` event. The kernel uses this to keep\n\t * its composition snapshot in sync with the controller's view.\n\t *\n\t * The bridge is responsible for unsubscribing on dispose / cohort\n\t * change so a stale controller doesn't keep emitting after rollover.\n\t */\n\tonCompositionChanged?: (composition: unknown) => void;\n\t/**\n\t * Optional callback invoked once with the resolved controller handle\n\t * the moment it is available. Lets the host keep a reference for\n\t * direct method calls (e.g. `navigateToItem`, `applySession`)\n\t * without re-querying the coordinator.\n\t */\n\tonControllerResolved?: (controller: SectionControllerHandle) => void;\n}\n\nexport interface CoordinatorBridgeOptions {\n\tcoordinator: CoordinatorPort;\n\tdispatchCoreInput: (input: SectionEngineInput) => void;\n}\n\nexport interface CoordinatorBridgeHandle {\n\tresolveSectionController(args: ResolveSectionControllerArgs): Promise<void>;\n\tdispose(): Promise<void>;\n}\n\nexport function createCoordinatorBridge(\n\toptions: CoordinatorBridgeOptions,\n): CoordinatorBridgeHandle {\n\tconst { coordinator, dispatchCoreInput } = options;\n\n\tlet activeInitToken = 0;\n\tlet activeCohort: CohortKey | null = null;\n\tlet unsubscribeController: (() => void) | null = null;\n\tlet disposed = false;\n\n\tconst unsubscribeFrameworkErrors = coordinator.subscribeFrameworkErrors(\n\t\t(model) => {\n\t\t\ttry {\n\t\t\t\tdispatchCoreInput({ kind: \"framework-error\", error: model });\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"[CoordinatorBridge] framework-error dispatch failed:\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t);\n\n\tfunction detachControllerSubscription(): void {\n\t\tif (!unsubscribeController) return;\n\t\ttry {\n\t\t\tunsubscribeController();\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[CoordinatorBridge] controller unsubscribe failed:\", error);\n\t\t}\n\t\tunsubscribeController = null;\n\t}\n\n\tasync function resolveSectionController(\n\t\targs: ResolveSectionControllerArgs,\n\t): Promise<void> {\n\t\tif (disposed) return;\n\t\tactiveInitToken += 1;\n\t\tconst token = activeInitToken;\n\t\tactiveCohort = args.cohort;\n\n\t\tconst controller = await coordinator.getOrCreateSectionController({\n\t\t\tsectionId: args.cohort.sectionId,\n\t\t\tattemptId: args.cohort.attemptId || undefined,\n\t\t\tinput: {\n\t\t\t\tsection: args.section,\n\t\t\t\tsectionId: args.cohort.sectionId,\n\t\t\t\tassessmentId: args.assessmentId,\n\t\t\t\tview: args.view,\n\t\t\t},\n\t\t\tupdateExisting: true,\n\t\t\tcreateDefaultController: args.createDefaultController,\n\t\t});\n\n\t\t// Drop late resolutions — a cohort change or dispose may have\n\t\t// rolled the token forward while we awaited the coordinator.\n\t\tif (token !== activeInitToken || disposed) return;\n\n\t\tdetachControllerSubscription();\n\n\t\targs.onControllerResolved?.(controller);\n\n\t\tconst getCompositionModel = (\n\t\t\tcontroller as SectionControllerHandle & {\n\t\t\t\tgetCompositionModel?: () => unknown;\n\t\t\t}\n\t\t).getCompositionModel;\n\t\targs.onCompositionChanged?.(getCompositionModel?.());\n\n\t\tif (typeof controller.subscribe === \"function\") {\n\t\t\tunsubscribeController = controller.subscribe(\n\t\t\t\t(_event: SectionControllerEvent) => {\n\t\t\t\t\tif (token !== activeInitToken || disposed) return;\n\t\t\t\t\targs.onCompositionChanged?.(getCompositionModel?.());\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tdispatchCoreInput({ kind: \"section-controller-resolved\" });\n\t}\n\n\tasync function dispose(): Promise<void> {\n\t\tif (disposed) return;\n\t\tdisposed = true;\n\t\tactiveInitToken += 1;\n\t\tdetachControllerSubscription();\n\t\ttry {\n\t\t\tunsubscribeFrameworkErrors();\n\t\t} catch (error) {\n\t\t\tconsole.warn(\n\t\t\t\t\"[CoordinatorBridge] framework-error unsubscribe failed:\",\n\t\t\t\terror,\n\t\t\t);\n\t\t}\n\t\tconst cohort = activeCohort;\n\t\tactiveCohort = null;\n\t\tif (cohort) {\n\t\t\ttry {\n\t\t\t\tawait coordinator.disposeSectionController({\n\t\t\t\t\tsectionId: cohort.sectionId,\n\t\t\t\t\tattemptId: cohort.attemptId || undefined,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t\"[CoordinatorBridge] section-controller dispose failed:\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { resolveSectionController, dispose };\n}\n"]}
@@ -14,7 +14,7 @@
14
14
  * outputs (`{ stage, status, cohort }`, `{ cohort, itemCount,
15
15
  * loadedCount }`). The bridge enriches them with `runtimeId`,
16
16
  * `sourceCe`, and `timestamp` so the dispatched DOM event detail
17
- * matches the legacy
17
+ * matches the canonical
18
18
  * `packages/players-shared/src/pie/stages.ts#StageChangeDetail` and
19
19
  * `LoadingCompleteDetail` shapes verbatim. `runtimeId` and `sourceCe`
20
20
  * are supplied at adapter construction time (one per engine instance);
@@ -14,7 +14,7 @@
14
14
  * outputs (`{ stage, status, cohort }`, `{ cohort, itemCount,
15
15
  * loadedCount }`). The bridge enriches them with `runtimeId`,
16
16
  * `sourceCe`, and `timestamp` so the dispatched DOM event detail
17
- * matches the legacy
17
+ * matches the canonical
18
18
  * `packages/players-shared/src/pie/stages.ts#StageChangeDetail` and
19
19
  * `LoadingCompleteDetail` shapes verbatim. `runtimeId` and `sourceCe`
20
20
  * are supplied at adapter construction time (one per engine instance);