@pie-players/pie-assessment-toolkit 0.3.61 → 0.3.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/dist/attempt/AssessmentSession.js +0 -1
  2. package/dist/attempt/TestSession.js +0 -1
  3. package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -1
  4. package/dist/components/ItemToolBar.custom-element.js +1 -10106
  5. package/dist/components/PieAssessmentToolkit.custom-element.js +48 -29930
  6. package/dist/components/SectionToolBar.custom-element.js +1 -14372
  7. package/dist/components/chunks/ItemToolBar-843902tp.js +51 -0
  8. package/dist/components/chunks/ItemToolBar-84nv78dy.js +22 -0
  9. package/dist/components/item-toolbar-element.js +0 -1
  10. package/dist/components/pie-assessment-toolkit-element.js +0 -1
  11. package/dist/components/section-toolbar-element.js +0 -1
  12. package/dist/context/assessment-toolkit-context.js +0 -1
  13. package/dist/context/runtime-context-consumer.js +0 -1
  14. package/dist/index.js +0 -1
  15. package/dist/item-loader.js +0 -1
  16. package/dist/policy/core/PolicySource.js +0 -1
  17. package/dist/policy/core/ToolPolicyEngine.js +0 -1
  18. package/dist/policy/core/compose-decision.js +0 -1
  19. package/dist/policy/core/decision-types.js +0 -1
  20. package/dist/policy/core/engine-context.js +0 -1
  21. package/dist/policy/core/pnp-policy-inputs.js +0 -1
  22. package/dist/policy/core/policy-source-tag.js +0 -1
  23. package/dist/policy/core/provenance.js +0 -1
  24. package/dist/policy/engine.js +0 -1
  25. package/dist/policy/internal.js +0 -1
  26. package/dist/policy/sources/PnpPolicySource.js +0 -1
  27. package/dist/runtime/RuntimeRegistry.js +0 -1
  28. package/dist/runtime/SectionRuntimeEngine.js +0 -1
  29. package/dist/runtime/adapter/SectionEngineAdapter.js +0 -1
  30. package/dist/runtime/adapter/coordinator-bridge.js +0 -1
  31. package/dist/runtime/adapter/dom-event-bridge.js +0 -1
  32. package/dist/runtime/adapter/framework-error-bridge.js +0 -1
  33. package/dist/runtime/adapter/instrumentation-bridge.js +0 -1
  34. package/dist/runtime/adapter/subscriber-fanout.js +0 -1
  35. package/dist/runtime/catalog-registration.js +0 -1
  36. package/dist/runtime/core/SectionEngineCore.js +0 -1
  37. package/dist/runtime/core/cohort.js +0 -1
  38. package/dist/runtime/core/engine-input.js +0 -1
  39. package/dist/runtime/core/engine-output.js +0 -1
  40. package/dist/runtime/core/engine-readiness.js +0 -1
  41. package/dist/runtime/core/engine-resolver.js +0 -1
  42. package/dist/runtime/core/engine-stage-derivation.js +0 -1
  43. package/dist/runtime/core/engine-state.js +0 -1
  44. package/dist/runtime/core/engine-transition.js +0 -1
  45. package/dist/runtime/engine-context.js +0 -1
  46. package/dist/runtime/engine.js +0 -1
  47. package/dist/runtime/internal.js +0 -1
  48. package/dist/runtime/registration-events.js +0 -1
  49. package/dist/runtime/runtime-id.js +0 -1
  50. package/dist/runtime/section-runtime-engine-host-context.js +0 -1
  51. package/dist/runtime/session-event-emitter-policy.js +0 -1
  52. package/dist/runtime/stage-emit-gate.js +0 -1
  53. package/dist/runtime/tool-host-contract.js +0 -1
  54. package/dist/services/AccessibilityCatalogResolver.js +0 -1
  55. package/dist/services/ContextVariableStore.js +0 -1
  56. package/dist/services/ElementToolStateStore.js +0 -1
  57. package/dist/services/HighlightCoordinator.js +35 -3
  58. package/dist/services/I18nService.js +0 -1
  59. package/dist/services/RangeSerializer.js +0 -1
  60. package/dist/services/SSMLExtractor.js +0 -1
  61. package/dist/services/TTSService.js +0 -1
  62. package/dist/services/ThemeProvider.js +0 -1
  63. package/dist/services/ToolConfigResolver.js +0 -1
  64. package/dist/services/ToolCoordinator.js +0 -1
  65. package/dist/services/ToolRegistry.js +0 -1
  66. package/dist/services/ToolkitCoordinator.js +0 -1
  67. package/dist/services/create-tools-config.js +0 -1
  68. package/dist/services/createDefaultToolRegistry.js +0 -1
  69. package/dist/services/defaultPersonalNeedsProfile.js +0 -1
  70. package/dist/services/framework-error-bus.js +0 -1
  71. package/dist/services/framework-error.js +0 -1
  72. package/dist/services/interfaces.js +0 -1
  73. package/dist/services/pnp-standard-features.js +0 -1
  74. package/dist/services/section-controller-types.js +0 -1
  75. package/dist/services/tool-config-defaults.js +0 -1
  76. package/dist/services/tool-config-validation.js +0 -1
  77. package/dist/services/tool-context.js +0 -1
  78. package/dist/services/tool-instance-id.js +0 -1
  79. package/dist/services/tool-providers/DesmosToolProvider.js +0 -1
  80. package/dist/services/tool-providers/TTSToolProvider.js +0 -1
  81. package/dist/services/tool-providers/ToolProviderApi.js +0 -1
  82. package/dist/services/tool-providers/ToolProviderRegistry.js +0 -1
  83. package/dist/services/tool-providers/index.js +0 -1
  84. package/dist/services/toolbar-items.js +0 -1
  85. package/dist/services/tools-config-normalizer.js +0 -1
  86. package/dist/services/tts/browser-provider.js +0 -1
  87. package/dist/services/tts/catalog-span-alignment.js +0 -1
  88. package/dist/services/tts/generated-speech/assemble-plan.js +0 -1
  89. package/dist/services/tts/generated-speech/dom/build-dom-plan.js +0 -1
  90. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js +0 -1
  91. package/dist/services/tts/generated-speech/dom/types.js +0 -1
  92. package/dist/services/tts/generated-speech/index.js +0 -1
  93. package/dist/services/tts/generated-speech/math-speech-cache.js +0 -1
  94. package/dist/services/tts/generated-speech/types.js +0 -1
  95. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +0 -1
  96. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +0 -1
  97. package/dist/services/tts/highlight-pipeline/highlight-policy.js +0 -1
  98. package/dist/services/tts/highlight-pipeline/index.js +0 -1
  99. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js +0 -1
  100. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +0 -1
  101. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +0 -1
  102. package/dist/services/tts/highlight-pipeline/readable-region.js +0 -1
  103. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js +0 -1
  104. package/dist/services/tts/highlight-pipeline/types.js +0 -1
  105. package/dist/services/tts/highlight-pipeline/visible-map-range.js +0 -1
  106. package/dist/services/tts/highlight-target-resolver.js +0 -1
  107. package/dist/services/tts/math-alignment/index.js +0 -1
  108. package/dist/services/tts/math-alignment/mathml-tokenizer.js +0 -1
  109. package/dist/services/tts/math-alignment/range-resolver.js +0 -1
  110. package/dist/services/tts/math-alignment/sequence-aligner.js +0 -1
  111. package/dist/services/tts/math-alignment/speech-tokenizer.js +0 -1
  112. package/dist/services/tts/math-alignment/types.js +0 -1
  113. package/dist/services/tts/math-aware-text-processing.js +0 -1
  114. package/dist/services/tts/math-speech.js +0 -1
  115. package/dist/services/tts/mathml-sanitization.js +0 -1
  116. package/dist/services/tts/ssml/spoken-text.js +0 -1
  117. package/dist/services/tts/text-processing.js +0 -1
  118. package/dist/services/tts/text-segmentation.js +0 -1
  119. package/dist/services/tts/visual-line-ranges.js +0 -1
  120. package/dist/services/tts-control-events.js +0 -1
  121. package/dist/services/tts-runtime-config.js +0 -1
  122. package/dist/tools/calculators/desmos-provider.js +0 -1
  123. package/dist/tools/client.js +0 -1
  124. package/dist/tools/index.js +0 -1
  125. package/dist/tools/library-loader.js +0 -1
  126. package/dist/tools/registrations/accessibility-tools.js +0 -1
  127. package/dist/tools/registrations/calculator.js +0 -1
  128. package/dist/tools/registrations/interaction-tools.js +0 -1
  129. package/dist/tools/registrations/measurement-tools.js +0 -1
  130. package/dist/tools/registrations/subject-specific-tools.js +0 -1
  131. package/dist/tools/registrations/toolbar-registration-helpers.js +0 -1
  132. package/dist/tools/registrations/tts.js +0 -1
  133. package/dist/tools/response-discovery.js +0 -1
  134. package/dist/tools/tool-tag-map.js +0 -1
  135. package/dist/tools/types.js +0 -1
  136. package/dist/tools/variant-resolver.js +0 -1
  137. package/dist/utils/logger.js +0 -1
  138. package/package.json +13 -11
  139. package/dist/attempt/AssessmentSession.js.map +0 -1
  140. package/dist/attempt/TestSession.js.map +0 -1
  141. package/dist/attempt/adapters/activity-to-test-attempt-session.js.map +0 -1
  142. package/dist/components/item-toolbar-element.js.map +0 -1
  143. package/dist/components/pie-assessment-toolkit-element.js.map +0 -1
  144. package/dist/components/section-toolbar-element.js.map +0 -1
  145. package/dist/context/assessment-toolkit-context.js.map +0 -1
  146. package/dist/context/runtime-context-consumer.js.map +0 -1
  147. package/dist/index.js.map +0 -1
  148. package/dist/item-loader.js.map +0 -1
  149. package/dist/policy/core/PolicySource.js.map +0 -1
  150. package/dist/policy/core/ToolPolicyEngine.js.map +0 -1
  151. package/dist/policy/core/compose-decision.js.map +0 -1
  152. package/dist/policy/core/decision-types.js.map +0 -1
  153. package/dist/policy/core/engine-context.js.map +0 -1
  154. package/dist/policy/core/pnp-policy-inputs.js.map +0 -1
  155. package/dist/policy/core/policy-source-tag.js.map +0 -1
  156. package/dist/policy/core/provenance.js.map +0 -1
  157. package/dist/policy/engine.js.map +0 -1
  158. package/dist/policy/internal.js.map +0 -1
  159. package/dist/policy/sources/PnpPolicySource.js.map +0 -1
  160. package/dist/runtime/RuntimeRegistry.js.map +0 -1
  161. package/dist/runtime/SectionRuntimeEngine.js.map +0 -1
  162. package/dist/runtime/adapter/SectionEngineAdapter.js.map +0 -1
  163. package/dist/runtime/adapter/coordinator-bridge.js.map +0 -1
  164. package/dist/runtime/adapter/dom-event-bridge.js.map +0 -1
  165. package/dist/runtime/adapter/framework-error-bridge.js.map +0 -1
  166. package/dist/runtime/adapter/instrumentation-bridge.js.map +0 -1
  167. package/dist/runtime/adapter/subscriber-fanout.js.map +0 -1
  168. package/dist/runtime/catalog-registration.js.map +0 -1
  169. package/dist/runtime/core/SectionEngineCore.js.map +0 -1
  170. package/dist/runtime/core/cohort.js.map +0 -1
  171. package/dist/runtime/core/engine-input.js.map +0 -1
  172. package/dist/runtime/core/engine-output.js.map +0 -1
  173. package/dist/runtime/core/engine-readiness.js.map +0 -1
  174. package/dist/runtime/core/engine-resolver.js.map +0 -1
  175. package/dist/runtime/core/engine-stage-derivation.js.map +0 -1
  176. package/dist/runtime/core/engine-state.js.map +0 -1
  177. package/dist/runtime/core/engine-transition.js.map +0 -1
  178. package/dist/runtime/engine-context.js.map +0 -1
  179. package/dist/runtime/engine.js.map +0 -1
  180. package/dist/runtime/internal.js.map +0 -1
  181. package/dist/runtime/registration-events.js.map +0 -1
  182. package/dist/runtime/runtime-id.js.map +0 -1
  183. package/dist/runtime/section-runtime-engine-host-context.js.map +0 -1
  184. package/dist/runtime/session-event-emitter-policy.js.map +0 -1
  185. package/dist/runtime/stage-emit-gate.js.map +0 -1
  186. package/dist/runtime/tool-host-contract.js.map +0 -1
  187. package/dist/services/AccessibilityCatalogResolver.js.map +0 -1
  188. package/dist/services/ContextVariableStore.js.map +0 -1
  189. package/dist/services/ElementToolStateStore.js.map +0 -1
  190. package/dist/services/HighlightCoordinator.js.map +0 -1
  191. package/dist/services/I18nService.js.map +0 -1
  192. package/dist/services/RangeSerializer.js.map +0 -1
  193. package/dist/services/SSMLExtractor.js.map +0 -1
  194. package/dist/services/TTSService.js.map +0 -1
  195. package/dist/services/ThemeProvider.js.map +0 -1
  196. package/dist/services/ToolConfigResolver.js.map +0 -1
  197. package/dist/services/ToolCoordinator.js.map +0 -1
  198. package/dist/services/ToolRegistry.js.map +0 -1
  199. package/dist/services/ToolkitCoordinator.js.map +0 -1
  200. package/dist/services/create-tools-config.js.map +0 -1
  201. package/dist/services/createDefaultToolRegistry.js.map +0 -1
  202. package/dist/services/defaultPersonalNeedsProfile.js.map +0 -1
  203. package/dist/services/framework-error-bus.js.map +0 -1
  204. package/dist/services/framework-error.js.map +0 -1
  205. package/dist/services/interfaces.js.map +0 -1
  206. package/dist/services/pnp-standard-features.js.map +0 -1
  207. package/dist/services/section-controller-types.js.map +0 -1
  208. package/dist/services/tool-config-defaults.js.map +0 -1
  209. package/dist/services/tool-config-validation.js.map +0 -1
  210. package/dist/services/tool-context.js.map +0 -1
  211. package/dist/services/tool-instance-id.js.map +0 -1
  212. package/dist/services/tool-providers/DesmosToolProvider.js.map +0 -1
  213. package/dist/services/tool-providers/TTSToolProvider.js.map +0 -1
  214. package/dist/services/tool-providers/ToolProviderApi.js.map +0 -1
  215. package/dist/services/tool-providers/ToolProviderRegistry.js.map +0 -1
  216. package/dist/services/tool-providers/index.js.map +0 -1
  217. package/dist/services/toolbar-items.js.map +0 -1
  218. package/dist/services/tools-config-normalizer.js.map +0 -1
  219. package/dist/services/tts/browser-provider.js.map +0 -1
  220. package/dist/services/tts/catalog-span-alignment.js.map +0 -1
  221. package/dist/services/tts/generated-speech/assemble-plan.js.map +0 -1
  222. package/dist/services/tts/generated-speech/dom/build-dom-plan.js.map +0 -1
  223. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js.map +0 -1
  224. package/dist/services/tts/generated-speech/dom/types.js.map +0 -1
  225. package/dist/services/tts/generated-speech/index.js.map +0 -1
  226. package/dist/services/tts/generated-speech/math-speech-cache.js.map +0 -1
  227. package/dist/services/tts/generated-speech/types.js.map +0 -1
  228. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js.map +0 -1
  229. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js.map +0 -1
  230. package/dist/services/tts/highlight-pipeline/highlight-policy.js.map +0 -1
  231. package/dist/services/tts/highlight-pipeline/index.js.map +0 -1
  232. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js.map +0 -1
  233. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js.map +0 -1
  234. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js.map +0 -1
  235. package/dist/services/tts/highlight-pipeline/readable-region.js.map +0 -1
  236. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js.map +0 -1
  237. package/dist/services/tts/highlight-pipeline/types.js.map +0 -1
  238. package/dist/services/tts/highlight-pipeline/visible-map-range.js.map +0 -1
  239. package/dist/services/tts/highlight-target-resolver.js.map +0 -1
  240. package/dist/services/tts/math-alignment/index.js.map +0 -1
  241. package/dist/services/tts/math-alignment/mathml-tokenizer.js.map +0 -1
  242. package/dist/services/tts/math-alignment/range-resolver.js.map +0 -1
  243. package/dist/services/tts/math-alignment/sequence-aligner.js.map +0 -1
  244. package/dist/services/tts/math-alignment/speech-tokenizer.js.map +0 -1
  245. package/dist/services/tts/math-alignment/types.js.map +0 -1
  246. package/dist/services/tts/math-aware-text-processing.js.map +0 -1
  247. package/dist/services/tts/math-speech.js.map +0 -1
  248. package/dist/services/tts/mathml-sanitization.js.map +0 -1
  249. package/dist/services/tts/ssml/spoken-text.js.map +0 -1
  250. package/dist/services/tts/text-processing.js.map +0 -1
  251. package/dist/services/tts/text-segmentation.js.map +0 -1
  252. package/dist/services/tts/visual-line-ranges.js.map +0 -1
  253. package/dist/services/tts-control-events.js.map +0 -1
  254. package/dist/services/tts-runtime-config.js.map +0 -1
  255. package/dist/tools/calculators/desmos-provider.js.map +0 -1
  256. package/dist/tools/client.js.map +0 -1
  257. package/dist/tools/index.js.map +0 -1
  258. package/dist/tools/library-loader.js.map +0 -1
  259. package/dist/tools/registrations/accessibility-tools.js.map +0 -1
  260. package/dist/tools/registrations/calculator.js.map +0 -1
  261. package/dist/tools/registrations/interaction-tools.js.map +0 -1
  262. package/dist/tools/registrations/measurement-tools.js.map +0 -1
  263. package/dist/tools/registrations/subject-specific-tools.js.map +0 -1
  264. package/dist/tools/registrations/toolbar-registration-helpers.js.map +0 -1
  265. package/dist/tools/registrations/tts.js.map +0 -1
  266. package/dist/tools/response-discovery.js.map +0 -1
  267. package/dist/tools/tool-tag-map.js.map +0 -1
  268. package/dist/tools/types.js.map +0 -1
  269. package/dist/tools/variant-resolver.js.map +0 -1
  270. package/dist/utils/logger.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-config-validation.js","sourceRoot":"","sources":["../../src/services/tool-config-validation.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tnormalizeToolsConfig,\n\ttype CanonicalToolsConfig,\n\ttype ToolPlacementLevel,\n\ttype ToolProviderConfig,\n\ttype TextToSpeechToolProviderConfig,\n} from \"./tools-config-normalizer.js\";\nimport {\n\tframeworkErrorFromToolConfigDiagnostics,\n\tframeworkErrorFromUnknown,\n\ttype FrameworkErrorModel,\n} from \"./framework-error.js\";\nimport type { ToolRegistration, ToolRegistry } from \"./ToolRegistry.js\";\nimport { createPackagedToolRegistry } from \"./createDefaultToolRegistry.js\";\n\nexport type ToolConfigStrictness = \"off\" | \"warn\" | \"error\";\n\nexport type ToolConfigDiagnosticSeverity = \"warning\" | \"error\";\n\nexport interface ToolConfigDiagnostic {\n\tcode:\n\t\t| \"tools.unknownToolId\"\n\t\t| \"tools.unsupportedLevel\"\n\t\t| \"tools.unknownProviderKey\"\n\t\t| \"tools.removedProviderKey\"\n\t\t| \"tools.providerSanitizeFailed\"\n\t\t| \"tools.providerValidateFailed\"\n\t\t| \"tools.invalidProviderValidation\";\n\tseverity: ToolConfigDiagnosticSeverity;\n\tpath: string;\n\tmessage: string;\n\ttoolId?: string;\n\tproviderId?: string;\n}\n\nexport interface ToolConfigValidationOptions {\n\tstrictness?: ToolConfigStrictness;\n\tsource?: string;\n\ttoolRegistry?: ToolRegistry | null;\n}\n\nexport interface ToolConfigValidationResult {\n\tconfig: CanonicalToolsConfig;\n\tdiagnostics: ToolConfigDiagnostic[];\n}\n\nexport function frameworkErrorFromToolConfigValidation(args: {\n\tsource: string;\n\tdiagnostics?: ToolConfigDiagnostic[] | null;\n\terror?: unknown;\n\trecoverable?: boolean;\n}): FrameworkErrorModel {\n\tif (Array.isArray(args.diagnostics) && args.diagnostics.length > 0) {\n\t\treturn frameworkErrorFromToolConfigDiagnostics({\n\t\t\tsource: args.source,\n\t\t\tdiagnostics: args.diagnostics,\n\t\t\trecoverable: args.recoverable,\n\t\t});\n\t}\n\treturn frameworkErrorFromUnknown({\n\t\tkind: \"tool-config\",\n\t\tsource: args.source,\n\t\terror: args.error ?? new Error(\"Invalid tools config\"),\n\t\trecoverable: args.recoverable,\n\t});\n}\n\nconst DEFAULT_STRICTNESS: ToolConfigStrictness = \"error\";\n\nexport function normalizeToolConfigStrictness(\n\tvalue: unknown,\n): ToolConfigStrictness {\n\tif (value === \"off\" || value === \"warn\" || value === \"error\") {\n\t\treturn value;\n\t}\n\treturn DEFAULT_STRICTNESS;\n}\n\nfunction createDiagnostic(\n\tdiagnostic: ToolConfigDiagnostic,\n): ToolConfigDiagnostic {\n\treturn diagnostic;\n}\n\nfunction getRegistryToolMap(\n\ttoolRegistry: ToolRegistry | null | undefined,\n): Map<string, ToolRegistration> {\n\tif (!toolRegistry) return new Map();\n\treturn new Map(\n\t\ttoolRegistry\n\t\t\t.getAllTools()\n\t\t\t.slice()\n\t\t\t.sort((left, right) => left.toolId.localeCompare(right.toolId))\n\t\t\t.map((tool) => [tool.toolId, tool]),\n\t);\n}\n\nfunction sanitizeProviderConfig(\n\tproviderId: string,\n\tproviderConfig:\n\t\t| ToolProviderConfig\n\t\t| TextToSpeechToolProviderConfig\n\t\t| undefined,\n\ttool: ToolRegistration | undefined,\n\tdiagnostics: ToolConfigDiagnostic[],\n): ToolProviderConfig | TextToSpeechToolProviderConfig | undefined {\n\tif (!providerConfig || !tool?.provider?.sanitizeConfig) return providerConfig;\n\ttry {\n\t\tconst sanitized = tool.provider.sanitizeConfig(\n\t\t\tproviderConfig as ToolProviderConfig,\n\t\t);\n\t\tif (\n\t\t\t!sanitized ||\n\t\t\ttypeof sanitized !== \"object\" ||\n\t\t\tArray.isArray(sanitized)\n\t\t) {\n\t\t\tdiagnostics.push(\n\t\t\t\tcreateDiagnostic({\n\t\t\t\t\tcode: \"tools.providerSanitizeFailed\",\n\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\t\tmessage: `Provider sanitizer for \"${providerId}\" must return an object.`,\n\t\t\t\t\tproviderId,\n\t\t\t\t\ttoolId: providerId,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn providerConfig;\n\t\t}\n\t\treturn sanitized as ToolProviderConfig | TextToSpeechToolProviderConfig;\n\t} catch (error) {\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.providerSanitizeFailed\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\tmessage: `Provider sanitizer failed for \"${providerId}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`,\n\t\t\t\tproviderId,\n\t\t\t\ttoolId: providerId,\n\t\t\t}),\n\t\t);\n\t\treturn providerConfig;\n\t}\n}\n\nfunction validateProviderConfig(\n\tproviderId: string,\n\tproviderConfig:\n\t\t| ToolProviderConfig\n\t\t| TextToSpeechToolProviderConfig\n\t\t| undefined,\n\ttool: ToolRegistration | undefined,\n\tdiagnostics: ToolConfigDiagnostic[],\n): void {\n\tif (!providerConfig || !tool?.provider?.validateConfig) return;\n\ttry {\n\t\tconst providerDiagnostics = tool.provider.validateConfig(\n\t\t\tproviderConfig as ToolProviderConfig,\n\t\t);\n\t\tif (!Array.isArray(providerDiagnostics)) {\n\t\t\tdiagnostics.push(\n\t\t\t\tcreateDiagnostic({\n\t\t\t\t\tcode: \"tools.invalidProviderValidation\",\n\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\t\tmessage: `Provider validator for \"${providerId}\" must return an array.`,\n\t\t\t\t\tproviderId,\n\t\t\t\t\ttoolId: providerId,\n\t\t\t\t}),\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tfor (const diagnostic of providerDiagnostics) {\n\t\t\tdiagnostics.push({\n\t\t\t\t...diagnostic,\n\t\t\t\tpath: diagnostic.path || `providers.${providerId}`,\n\t\t\t\tproviderId: diagnostic.providerId || providerId,\n\t\t\t\ttoolId: diagnostic.toolId || providerId,\n\t\t\t});\n\t\t}\n\t} catch (error) {\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.providerValidateFailed\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\tmessage: `Provider validator failed for \"${providerId}\": ${\n\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t}`,\n\t\t\t\tproviderId,\n\t\t\t\ttoolId: providerId,\n\t\t\t}),\n\t\t);\n\t}\n}\n\nfunction collectPlacementDiagnostics(\n\tconfig: CanonicalToolsConfig,\n\ttoolMap: Map<string, ToolRegistration>,\n\tdiagnostics: ToolConfigDiagnostic[],\n): void {\n\tif (toolMap.size === 0) return;\n\tconst levelOrder: ToolPlacementLevel[] = [\"section\", \"item\", \"passage\"];\n\tfor (const level of levelOrder) {\n\t\tfor (const toolId of config.placement[level]) {\n\t\t\tconst tool = toolMap.get(toolId);\n\t\t\tif (!tool) {\n\t\t\t\tdiagnostics.push(\n\t\t\t\t\tcreateDiagnostic({\n\t\t\t\t\t\tcode: \"tools.unknownToolId\",\n\t\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\t\tpath: `placement.${level}`,\n\t\t\t\t\t\tmessage: `Unknown tool id \"${toolId}\" in placement.${level}.`,\n\t\t\t\t\t\ttoolId,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst supportsLevel = tool.supportedLevels.includes(level);\n\t\t\t// Leniency policy: section-capable tools are tolerated in item placement.\n\t\t\t// This avoids strict failures for common host config patterns while still\n\t\t\t// enforcing upward-invalid placements (for example item-only -> section).\n\t\t\tconst allowSectionToolInItemPlacement =\n\t\t\t\tlevel === \"item\" && tool.supportedLevels.includes(\"section\");\n\t\t\tif (!supportsLevel && !allowSectionToolInItemPlacement) {\n\t\t\t\tdiagnostics.push(\n\t\t\t\t\tcreateDiagnostic({\n\t\t\t\t\t\tcode: \"tools.unsupportedLevel\",\n\t\t\t\t\t\tseverity: \"error\",\n\t\t\t\t\t\tpath: `placement.${level}`,\n\t\t\t\t\t\tmessage: `Tool \"${toolId}\" does not support level \"${level}\".`,\n\t\t\t\t\t\ttoolId,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction collectPolicyDiagnostics(\n\tconfig: CanonicalToolsConfig,\n\ttoolMap: Map<string, ToolRegistration>,\n\tdiagnostics: ToolConfigDiagnostic[],\n): void {\n\tif (toolMap.size === 0) return;\n\tfor (const toolId of config.policy.allowed || []) {\n\t\tif (toolMap.has(toolId)) continue;\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.unknownToolId\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: \"policy.allowed\",\n\t\t\t\tmessage: `Unknown tool id \"${toolId}\" in policy.allowed.`,\n\t\t\t\ttoolId,\n\t\t\t}),\n\t\t);\n\t}\n\tfor (const toolId of config.policy.blocked || []) {\n\t\tif (toolMap.has(toolId)) continue;\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.unknownToolId\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: \"policy.blocked\",\n\t\t\t\tmessage: `Unknown tool id \"${toolId}\" in policy.blocked.`,\n\t\t\t\ttoolId,\n\t\t\t}),\n\t\t);\n\t}\n}\n\nfunction collectProviderKeyDiagnostics(\n\tconfig: CanonicalToolsConfig,\n\ttoolMap: Map<string, ToolRegistration>,\n\thasRemovedTtsKey: boolean,\n\tdiagnostics: ToolConfigDiagnostic[],\n): void {\n\tif (hasRemovedTtsKey) {\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.removedProviderKey\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: \"providers.tts\",\n\t\t\t\tmessage: `Provider key \"tts\" is no longer supported. Use \"providers.textToSpeech\".`,\n\t\t\t\tproviderId: \"tts\",\n\t\t\t\ttoolId: \"textToSpeech\",\n\t\t\t}),\n\t\t);\n\t}\n\tfor (const providerId of Object.keys(config.providers).sort()) {\n\t\tif (toolMap.size === 0 || toolMap.has(providerId)) continue;\n\t\tdiagnostics.push(\n\t\t\tcreateDiagnostic({\n\t\t\t\tcode: \"tools.unknownProviderKey\",\n\t\t\t\tseverity: \"error\",\n\t\t\t\tpath: `providers.${providerId}`,\n\t\t\t\tmessage: `Unknown provider key \"${providerId}\".`,\n\t\t\t\tproviderId,\n\t\t\t\ttoolId: providerId,\n\t\t\t}),\n\t\t);\n\t}\n}\n\nfunction emitWarnings(\n\tdiagnostics: ToolConfigDiagnostic[],\n\tsource: string,\n): void {\n\tif (diagnostics.length === 0) return;\n\tfor (const diagnostic of diagnostics) {\n\t\tconst prefix = `[tool-config-validation:${source}]`;\n\t\tconsole.warn(`${prefix} ${diagnostic.path} - ${diagnostic.message}`);\n\t}\n}\n\nfunction throwValidationError(\n\tdiagnostics: ToolConfigDiagnostic[],\n\tsource: string,\n): never {\n\tconst formatted = diagnostics\n\t\t.map((diagnostic) => `- ${diagnostic.path}: ${diagnostic.message}`)\n\t\t.join(\"\\n\");\n\tthrow new Error(\n\t\t`[tool-config-validation:${source}] Invalid tools config:\\n${formatted}`,\n\t);\n}\n\nexport function normalizeAndValidateToolsConfig(\n\tinput?: Partial<CanonicalToolsConfig> | null,\n\toptions: ToolConfigValidationOptions = {},\n): ToolConfigValidationResult {\n\tconst strictness = normalizeToolConfigStrictness(options.strictness);\n\tconst source = options.source ?? \"tools\";\n\tconst registryTools = getRegistryToolMap(\n\t\toptions.toolRegistry ?? createPackagedToolRegistry(),\n\t);\n\tconst normalized = normalizeToolsConfig(input);\n\tconst diagnostics: ToolConfigDiagnostic[] = [];\n\tconst hasRemovedTtsKey = Object.hasOwn(normalized.providers, \"tts\");\n\n\tconst nextProviders: CanonicalToolsConfig[\"providers\"] = {\n\t\t...(normalized.providers || {}),\n\t};\n\tfor (const providerId of Object.keys(nextProviders).sort()) {\n\t\tif (providerId === \"tts\") {\n\t\t\tdelete nextProviders.tts;\n\t\t\tcontinue;\n\t\t}\n\t\tconst tool = registryTools.get(providerId);\n\t\tnextProviders[providerId] = sanitizeProviderConfig(\n\t\t\tproviderId,\n\t\t\tnextProviders[providerId],\n\t\t\ttool,\n\t\t\tdiagnostics,\n\t\t);\n\t\tvalidateProviderConfig(\n\t\t\tproviderId,\n\t\t\tnextProviders[providerId],\n\t\t\ttool,\n\t\t\tdiagnostics,\n\t\t);\n\t}\n\tconst nextConfig: CanonicalToolsConfig = {\n\t\t...normalized,\n\t\tproviders: nextProviders,\n\t};\n\n\tcollectPlacementDiagnostics(nextConfig, registryTools, diagnostics);\n\tcollectPolicyDiagnostics(nextConfig, registryTools, diagnostics);\n\tcollectProviderKeyDiagnostics(\n\t\tnextConfig,\n\t\tregistryTools,\n\t\thasRemovedTtsKey,\n\t\tdiagnostics,\n\t);\n\tif (hasRemovedTtsKey) {\n\t\tthrowValidationError(\n\t\t\tdiagnostics.filter((entry) => entry.code === \"tools.removedProviderKey\"),\n\t\t\tsource,\n\t\t);\n\t}\n\n\tif (strictness === \"warn\") {\n\t\temitWarnings(diagnostics, source);\n\t}\n\tif (strictness === \"error\" && diagnostics.length > 0) {\n\t\tthrowValidationError(diagnostics, source);\n\t}\n\n\treturn {\n\t\tconfig: nextConfig,\n\t\tdiagnostics,\n\t};\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-context.js","sourceRoot":"","sources":["../../src/services/tool-context.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Tool Context Types\n *\n * Defines the context types passed to tools for making visibility and behavior decisions.\n * Each context level provides increasingly specific information about where the tool is being used.\n */\n\nimport type {\n\tAssessmentEntity,\n\tAssessmentItemRef,\n\tAssessmentSection,\n\tItemEntity,\n\tPassageEntity,\n\tRubricBlock,\n} from \"@pie-players/pie-players-shared/types\";\n\n/**\n * Tool placement levels - where a tool can appear in the UI\n */\nexport type ToolLevel =\n\t| \"assessment\" // Assessment-wide tools (e.g., global calculator)\n\t| \"section\" // Section toolbar tools\n\t| \"item\" // Item-level tools (question toolbar)\n\t| \"passage\" // Passage header tools (stimulus rubric blocks)\n\t| \"rubric\" // Rubric block tools (instructions, rubrics)\n\t| \"element\"; // Element-specific tools (inline with interaction)\n\n/**\n * Base context available to all tools\n */\nexport interface BaseToolContext {\n\t/** The level at which this tool is being evaluated */\n\tlevel: ToolLevel;\n\n\t/** The full assessment entity */\n\tassessment: AssessmentEntity;\n\n\t/** Current section, if applicable */\n\tsection?: AssessmentSection;\n\n\t/** Reference to the current item (always available except at assessment level) */\n\titemRef?: AssessmentItemRef;\n\n\t/** DOM container where the tool will be rendered */\n\tcontainer?: HTMLElement;\n}\n\n/**\n * Context for assessment-level tools\n */\nexport interface AssessmentToolContext extends BaseToolContext {\n\tlevel: \"assessment\";\n}\n\n/**\n * Context for section-level tools\n */\nexport interface SectionToolContext extends BaseToolContext {\n\tlevel: \"section\";\n\tsection: AssessmentSection;\n}\n\n/**\n * Context for item-level tools (question toolbar)\n */\nexport interface ItemToolContext extends BaseToolContext {\n\tlevel: \"item\";\n\titemRef: AssessmentItemRef;\n\titem: ItemEntity;\n\n\t/** Passage entity if this item is associated with a passage */\n\tpassage?: PassageEntity;\n}\n\n/**\n * Context for passage-level tools (passage header)\n *\n * Passages can appear as:\n * - Standalone passage entities associated with items\n * - RubricBlock with class=\"stimulus\" containing embedded passage\n */\nexport interface PassageToolContext extends BaseToolContext {\n\tlevel: \"passage\";\n\titemRef?: AssessmentItemRef;\n\tpassage: PassageEntity;\n\n\t/** Items associated with this passage (if applicable) */\n\titems?: ItemEntity[];\n\n\t/** RubricBlock containing this passage (if from rubric) */\n\trubricBlock?: RubricBlock;\n}\n\n/**\n * Context for rubric block tools\n *\n * RubricBlocks can contain:\n * - Instructions (class=\"instructions\")\n * - Rubrics (class=\"rubric\")\n * - Stimulus/passages (class=\"stimulus\" with embedded passage)\n * - Simple HTML content\n */\nexport interface RubricToolContext extends BaseToolContext {\n\tlevel: \"rubric\";\n\trubricBlock: RubricBlock;\n\n\t/** Section containing this rubric block */\n\tsection: AssessmentSection;\n\n\t/** If rubric contains a passage (class=\"stimulus\") */\n\tpassage?: PassageEntity;\n}\n\n/**\n * Context for element-level tools (inline with interaction)\n *\n * Note: Element-level context uses the PIE interaction element ID\n * and provides access to the full item config for content inspection.\n */\nexport interface ElementToolContext extends BaseToolContext {\n\tlevel: \"element\";\n\titemRef: AssessmentItemRef;\n\titem: ItemEntity;\n\n\t/** The PIE element/interaction ID */\n\telementId: string;\n\n\t/** Passage entity if this item is associated with a passage */\n\tpassage?: PassageEntity;\n\n\t/** DOM element that triggered the tool (e.g., the specific input field) */\n\ttriggerElement?: HTMLElement;\n}\n\n/**\n * Union type of all tool contexts\n */\nexport type ToolContext =\n\t| AssessmentToolContext\n\t| SectionToolContext\n\t| ItemToolContext\n\t| PassageToolContext\n\t| RubricToolContext\n\t| ElementToolContext;\n\n/**\n * Type guard to check if context is at assessment level\n */\nexport function isAssessmentContext(\n\tcontext: ToolContext,\n): context is AssessmentToolContext {\n\treturn context.level === \"assessment\";\n}\n\n/**\n * Type guard to check if context is at section level\n */\nexport function isSectionContext(\n\tcontext: ToolContext,\n): context is SectionToolContext {\n\treturn context.level === \"section\";\n}\n\n/**\n * Type guard to check if context is at item level\n */\nexport function isItemContext(\n\tcontext: ToolContext,\n): context is ItemToolContext {\n\treturn context.level === \"item\";\n}\n\n/**\n * Type guard to check if context is at passage level\n */\nexport function isPassageContext(\n\tcontext: ToolContext,\n): context is PassageToolContext {\n\treturn context.level === \"passage\";\n}\n\n/**\n * Type guard to check if context is at rubric level\n */\nexport function isRubricContext(\n\tcontext: ToolContext,\n): context is RubricToolContext {\n\treturn context.level === \"rubric\";\n}\n\n/**\n * Type guard to check if context is at element level\n */\nexport function isElementContext(\n\tcontext: ToolContext,\n): context is ElementToolContext {\n\treturn context.level === \"element\";\n}\n\n/**\n * Helper to extract text content from an item or element for analysis\n */\nexport function extractTextContent(context: ToolContext): string {\n\tif (isElementContext(context)) {\n\t\tconst config = context.item.config;\n\t\tif (!config) return \"\";\n\t\tconst textChunks: string[] = [];\n\t\tconst stripHtml = (value: string) => value.replace(/<[^>]*>/g, \" \").trim();\n\n\t\t// Try to find element markup by element id.\n\t\tconst elementMarkup = config.elements?.[context.elementId];\n\t\tif (elementMarkup) {\n\t\t\tif (typeof elementMarkup === \"string\") {\n\t\t\t\ttextChunks.push(stripHtml(elementMarkup));\n\t\t\t}\n\t\t}\n\n\t\t// Also inspect model data keyed by this element id.\n\t\t// In many items, math appears in model.prompt/labels rather than elements[elementId].\n\t\tconst modelsRaw = config.models;\n\t\tconst models = Array.isArray(modelsRaw)\n\t\t\t? modelsRaw\n\t\t\t: modelsRaw && typeof modelsRaw === \"object\"\n\t\t\t\t? Object.values(modelsRaw as Record<string, unknown>)\n\t\t\t\t: [];\n\t\tconst model = models.find(\n\t\t\t(m: any) => m && typeof m === \"object\" && m.id === context.elementId,\n\t\t) as Record<string, unknown> | undefined;\n\t\tif (model) {\n\t\t\tfor (const value of Object.values(model)) {\n\t\t\t\tif (typeof value === \"string\") {\n\t\t\t\t\ttextChunks.push(stripHtml(value));\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\tfor (const entry of value) {\n\t\t\t\t\t\tif (entry && typeof entry === \"object\") {\n\t\t\t\t\t\t\tfor (const nested of Object.values(\n\t\t\t\t\t\t\t\tentry as Record<string, unknown>,\n\t\t\t\t\t\t\t)) {\n\t\t\t\t\t\t\t\tif (typeof nested === \"string\") {\n\t\t\t\t\t\t\t\t\ttextChunks.push(stripHtml(nested));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn textChunks.filter(Boolean).join(\" \").trim();\n\t}\n\n\tif (isItemContext(context)) {\n\t\tconst item = context.item;\n\t\tif (!item?.config) return \"\";\n\n\t\tconst config = item.config as Record<string, unknown>;\n\t\tconst textChunks: string[] = [];\n\t\tconst stripHtml = (value: string) => value.replace(/<[^>]*>/g, \" \").trim();\n\n\t\t// Primary item markup\n\t\tif (typeof config.markup === \"string\") {\n\t\t\ttextChunks.push(stripHtml(config.markup));\n\t\t}\n\n\t\t// Element markup snippets\n\t\tconst elements = config.elements as Record<string, unknown> | undefined;\n\t\tif (elements && typeof elements === \"object\") {\n\t\t\tfor (const elementMarkup of Object.values(elements)) {\n\t\t\t\tif (typeof elementMarkup === \"string\") {\n\t\t\t\t\ttextChunks.push(stripHtml(elementMarkup));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Model-level text (prompts, labels, etc.)\n\t\tconst modelsRaw = config.models;\n\t\tconst models = Array.isArray(modelsRaw)\n\t\t\t? modelsRaw\n\t\t\t: modelsRaw && typeof modelsRaw === \"object\"\n\t\t\t\t? Object.values(modelsRaw as Record<string, unknown>)\n\t\t\t\t: [];\n\t\tfor (const model of models) {\n\t\t\tif (!model || typeof model !== \"object\") continue;\n\t\t\tfor (const value of Object.values(model as Record<string, unknown>)) {\n\t\t\t\tif (typeof value === \"string\") {\n\t\t\t\t\ttextChunks.push(stripHtml(value));\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\tfor (const entry of value) {\n\t\t\t\t\t\tif (entry && typeof entry === \"object\") {\n\t\t\t\t\t\t\tfor (const nested of Object.values(\n\t\t\t\t\t\t\t\tentry as Record<string, unknown>,\n\t\t\t\t\t\t\t)) {\n\t\t\t\t\t\t\t\tif (typeof nested === \"string\") {\n\t\t\t\t\t\t\t\t\ttextChunks.push(stripHtml(nested));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn textChunks.filter(Boolean).join(\" \").trim();\n\t}\n\n\tif (isPassageContext(context)) {\n\t\tconst passage = context.passage;\n\t\tif (!passage?.config) return \"\";\n\n\t\tconst config = passage.config as Record<string, unknown>;\n\t\tconst textChunks: string[] = [];\n\t\tconst stripHtml = (value: string) => value.replace(/<[^>]*>/g, \" \").trim();\n\n\t\t// Primary passage markup/content\n\t\tif (typeof config.markup === \"string\") {\n\t\t\ttextChunks.push(stripHtml(config.markup));\n\t\t}\n\t\tif (typeof config.content === \"string\") {\n\t\t\ttextChunks.push(stripHtml(config.content));\n\t\t}\n\t\tif (typeof config.prompt === \"string\") {\n\t\t\ttextChunks.push(stripHtml(config.prompt));\n\t\t}\n\n\t\t// Element markup snippets\n\t\tconst elements = config.elements as Record<string, unknown> | undefined;\n\t\tif (elements && typeof elements === \"object\") {\n\t\t\tfor (const elementMarkup of Object.values(elements)) {\n\t\t\t\tif (typeof elementMarkup === \"string\") {\n\t\t\t\t\ttextChunks.push(stripHtml(elementMarkup));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Model-level text (prompts, labels, etc.)\n\t\tconst modelsRaw = config.models;\n\t\tconst models = Array.isArray(modelsRaw)\n\t\t\t? modelsRaw\n\t\t\t: modelsRaw && typeof modelsRaw === \"object\"\n\t\t\t\t? Object.values(modelsRaw as Record<string, unknown>)\n\t\t\t\t: [];\n\t\tfor (const model of models) {\n\t\t\tif (!model || typeof model !== \"object\") continue;\n\t\t\tfor (const value of Object.values(model as Record<string, unknown>)) {\n\t\t\t\tif (typeof value === \"string\") {\n\t\t\t\t\ttextChunks.push(stripHtml(value));\n\t\t\t\t}\n\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\tfor (const entry of value) {\n\t\t\t\t\t\tif (entry && typeof entry === \"object\") {\n\t\t\t\t\t\t\tfor (const nested of Object.values(\n\t\t\t\t\t\t\t\tentry as Record<string, unknown>,\n\t\t\t\t\t\t\t)) {\n\t\t\t\t\t\t\t\tif (typeof nested === \"string\") {\n\t\t\t\t\t\t\t\t\ttextChunks.push(stripHtml(nested));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn textChunks.filter(Boolean).join(\" \").trim();\n\t}\n\n\tif (isRubricContext(context)) {\n\t\tconst rubric = context.rubricBlock;\n\n\t\t// If rubric has embedded passage, extract from passage config\n\t\tif (rubric.passage?.config) {\n\t\t\tconst markup = rubric.passage.config.markup || \"\";\n\t\t\treturn markup.replace(/<[^>]*>/g, \" \").trim();\n\t\t}\n\n\t\t// Otherwise, use simple content string\n\t\tconst content = rubric.content || \"\";\n\t\treturn content.replace(/<[^>]*>/g, \" \").trim();\n\t}\n\n\treturn \"\";\n}\n\n/**\n * Helper to check if context contains mathematical content\n * (Basic heuristic - can be overridden by tools)\n */\nexport function hasMathContent(context: ToolContext): boolean {\n\tconst text = extractTextContent(context);\n\n\t// Look for common math indicators\n\tconst mathIndicators = [\n\t\t/<math[>\\s]/i, // MathML\n\t\t/\\\\\\[([^\\]]+)\\\\\\]/, // LaTeX display math\n\t\t/\\$\\$[^$]+\\$\\$/, // LaTeX display math ($$...$$)\n\t\t/\\\\\\(/, // LaTeX inline math\n\t\t/[+\\-*/=<>≤≥∑∫√π]/, // Math symbols\n\t\t/\\d+\\s*[+\\-*/=]\\s*\\d+/, // Simple arithmetic\n\t];\n\n\treturn mathIndicators.some((pattern) => pattern.test(text));\n}\n\n/**\n * Helper to check if context contains choice-based interactions\n */\nexport function hasChoiceInteraction(context: ToolContext): boolean {\n\tconst interactionTypes = [\n\t\t\"pie-multiple-choice\",\n\t\t\"pie-inline-choice\",\n\t\t\"pie-select-text\",\n\t\t\"pie-ebsr\",\n\t\t\"multiple-choice\",\n\t\t\"inline-choice\",\n\t\t\"select-text\",\n\t\t\"ebsr\",\n\t];\n\n\tif (isElementContext(context)) {\n\t\tconst config = context.item.config;\n\t\tif (!config?.models) return false;\n\n\t\t// Find model for this element\n\t\tconst models = Array.isArray(config.models)\n\t\t\t? config.models\n\t\t\t: Object.values(config.models as Record<string, unknown>);\n\t\tconst model = models.find(\n\t\t\t(m: any) => m && typeof m === \"object\" && m.id === context.elementId,\n\t\t);\n\t\tif (!model) return false;\n\n\t\tconst type = (model as any).element || \"\";\n\t\treturn interactionTypes.includes(type);\n\t}\n\n\tif (isItemContext(context)) {\n\t\tconst modelsRaw = context.item.config?.models;\n\t\tconst models = Array.isArray(modelsRaw)\n\t\t\t? modelsRaw\n\t\t\t: modelsRaw && typeof modelsRaw === \"object\"\n\t\t\t\t? Object.values(modelsRaw as Record<string, unknown>)\n\t\t\t\t: [];\n\t\treturn models.some((m: any) => {\n\t\t\tif (!m || typeof m !== \"object\") return false;\n\t\t\tconst type = m.element || \"\";\n\t\t\tif (interactionTypes.includes(type)) return true;\n\t\t\t// Fallback for configs that don't provide canonical element names.\n\t\t\treturn Array.isArray(m.choices) && m.choices.length > 0;\n\t\t});\n\t}\n\n\treturn false;\n}\n\n/**\n * Helper to check if context contains text that can be read aloud\n */\nexport function hasReadableText(context: ToolContext): boolean {\n\tconst text = extractTextContent(context);\n\t// Must have at least 10 characters of text (arbitrary threshold)\n\treturn text.trim().length >= 10;\n}\n\n/**\n * Helper to check if context contains science content\n * (Basic heuristic - can be overridden by tools)\n */\nexport function hasScienceContent(context: ToolContext): boolean {\n\tconst text = extractTextContent(context);\n\n\t// Look for common science indicators\n\tconst scienceIndicators = [\n\t\t/chemistry|chemical|element|atom|molecule|compound/i,\n\t\t/periodic\\s+table/i,\n\t\t/H₂O|CO₂|NaCl|O₂|N₂/i, // Chemical formulas\n\t\t/\\b[A-Z][a-z]?\\d*\\b/, // Element symbols (H, He, Li, etc.)\n\t\t/biology|organism|cell|DNA|RNA|protein/i,\n\t\t/physics|force|energy|velocity|acceleration/i,\n\t];\n\n\treturn scienceIndicators.some((pattern) => pattern.test(text));\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tool-instance-id.js","sourceRoot":"","sources":["../../src/services/tool-instance-id.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACxC,YAAY;IACZ,SAAS;IACT,MAAM;IACN,SAAS;IACT,QAAQ;CACC,CAAC;AAWX,MAAM,yBAAyB,GAAG,IAAI,GAAG,CAAS,yBAAyB,CAAC,CAAC;AAE7E,MAAM,UAAU,4BAA4B;IAC3C,OAAO,KAAK,CAAC,IAAI,CAAC,yBAAyB,CAAC,MAAM,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,UAAkB;IACxD,MAAM,UAAU,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IAChE,CAAC;IACD,yBAAyB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,0BAA0B,CACzC,UAAkB;IAElB,OAAO,yBAAyB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,kBAAkB,CACjC,UAAkB,EAClB,UAA0B,EAC1B,OAAe;IAEf,MAAM,cAAc,GAAG,UAAU,CAAC,IAAI,EAAE,CAAC;IACzC,MAAM,iBAAiB,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IACzC,IAAI,CAAC,cAAc,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,CAAC,0BAA0B,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CACd,6BAA6B,UAAU,0DAA0D,CACjG,CAAC;IACH,CAAC;IACD,OAAO,GAAG,cAAc,IAAI,UAAU,IAAI,iBAAiB,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,EAAU;IAC3C,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC5B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,CAAC,UAAU,EAAE,aAAa,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC;IACnD,IAAI,CAAC,UAAU,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACzC,IAAI,CAAC,0BAA0B,CAAC,aAAa,CAAC,EAAE,CAAC;QAChD,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO;QACN,UAAU;QACV,UAAU,EAAE,aAAa;QACzB,OAAO;KACP,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,EAAU;IACzC,MAAM,MAAM,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IACvB,OAAO,kBAAkB,CACxB,MAAM,CAAC,UAAU,EACjB,MAAM,CAAC,UAAU,EACjB,MAAM,CAAC,OAAO,CACd,CAAC;AACH,CAAC","sourcesContent":["export const DEFAULT_TOOL_SCOPE_LEVELS = [\n\t\"assessment\",\n\t\"section\",\n\t\"item\",\n\t\"passage\",\n\t\"rubric\",\n] as const;\n\nexport type BuiltinToolScopeLevel = (typeof DEFAULT_TOOL_SCOPE_LEVELS)[number];\nexport type ToolScopeLevel = BuiltinToolScopeLevel | (string & {});\n\nexport interface ParsedToolInstanceId {\n\tbaseToolId: string;\n\tscopeLevel: ToolScopeLevel;\n\tscopeId: string;\n}\n\nconst registeredToolScopeLevels = new Set<string>(DEFAULT_TOOL_SCOPE_LEVELS);\n\nexport function getRegisteredToolScopeLevels(): string[] {\n\treturn Array.from(registeredToolScopeLevels.values());\n}\n\nexport function registerToolScopeLevel(scopeLevel: string): void {\n\tconst normalized = scopeLevel.trim();\n\tif (!normalized) {\n\t\tthrow new Error(\"Tool scope level must be a non-empty string\");\n\t}\n\tregisteredToolScopeLevels.add(normalized);\n}\n\nexport function isRegisteredToolScopeLevel(\n\tscopeLevel: string,\n): scopeLevel is ToolScopeLevel {\n\treturn registeredToolScopeLevels.has(scopeLevel);\n}\n\nexport function createScopedToolId(\n\tbaseToolId: string,\n\tscopeLevel: ToolScopeLevel,\n\tscopeId: string,\n): string {\n\tconst normalizedBase = baseToolId.trim();\n\tconst normalizedScopeId = scopeId.trim();\n\tif (!normalizedBase || !normalizedScopeId) {\n\t\tthrow new Error(\"Tool instance ids require non-empty tool and scope ids\");\n\t}\n\tif (!isRegisteredToolScopeLevel(scopeLevel)) {\n\t\tthrow new Error(\n\t\t\t`Unknown tool scope level '${scopeLevel}'. Register custom levels with registerToolScopeLevel().`,\n\t\t);\n\t}\n\treturn `${normalizedBase}:${scopeLevel}:${normalizedScopeId}`;\n}\n\nexport function parseScopedToolId(id: string): ParsedToolInstanceId | null {\n\tconst parts = id.split(\":\");\n\tif (parts.length !== 3) return null;\n\tconst [baseToolId, scopeLevelRaw, scopeId] = parts;\n\tif (!baseToolId || !scopeId) return null;\n\tif (!isRegisteredToolScopeLevel(scopeLevelRaw)) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tbaseToolId,\n\t\tscopeLevel: scopeLevelRaw,\n\t\tscopeId,\n\t};\n}\n\nexport function toOverlayToolId(id: string): string {\n\tconst parsed = parseScopedToolId(id);\n\tif (!parsed) return id;\n\treturn createScopedToolId(\n\t\tparsed.baseToolId,\n\t\tparsed.scopeLevel,\n\t\tparsed.scopeId,\n\t);\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"DesmosToolProvider.js","sourceRoot":"","sources":["../../../src/services/tool-providers/DesmosToolProvider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Desmos Calculator Tool Provider\n *\n * Provides Desmos calculators (basic, scientific, graphing)\n * with authentication and proxy support.\n *\n * SECURITY BEST PRACTICE:\n * - Development: Pass apiKey directly for local testing\n * - Production: Use proxyEndpoint or authFetcher to keep API key server-side\n *\n * Part of PIE Assessment Toolkit.\n */\n\nimport type {\n\tCalculatorProvider,\n\tDesmosCalculatorConfig,\n} from \"@pie-players/pie-calculator\";\nimport type {\n\tToolProviderApi,\n\tToolProviderCapabilities,\n} from \"./ToolProviderApi.js\";\n\n/**\n * Desmos tool provider configuration\n */\nexport interface DesmosToolProviderConfig {\n\t/**\n\t * Desmos API key (DEVELOPMENT ONLY)\n\t * Never expose in production client code!\n\t *\n\t * Obtain from: https://www.desmos.com/api\n\t */\n\tapiKey?: string;\n\n\t/**\n\t * Server proxy endpoint (PRODUCTION RECOMMENDED)\n\t * Backend handles API key securely\n\t *\n\t * @example '/api/desmos/token'\n\t * @example 'https://api.myapp.com/tools/desmos/auth'\n\t */\n\tproxyEndpoint?: string;\n\n\t/**\n\t * Default calculator configuration applied to all instances\n\t */\n\tdefaultConfig?: DesmosCalculatorConfig;\n\n\t/**\n\t * Optional telemetry callback for tool/backend instrumentation.\n\t */\n\tonTelemetry?: (\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t) => void | Promise<void>;\n}\n\n/**\n * Desmos Calculator Tool Provider\n *\n * Wraps DesmosCalculatorProvider with the ToolProviderApi interface\n * for use in the ToolProviderRegistry.\n *\n * @example\n * ```typescript\n * const provider = new DesmosToolProvider();\n *\n * await provider.initialize({\n * apiKey: 'your-api-key', // Development only\n * proxyEndpoint: '/api/desmos/token', // Production\n * });\n *\n * const calculatorProvider = await provider.createInstance();\n * ```\n */\nexport class DesmosToolProvider\n\timplements ToolProviderApi<DesmosToolProviderConfig, CalculatorProvider>\n{\n\treadonly providerId = \"desmos-calculator\";\n\treadonly providerName = \"Desmos Calculator\";\n\treadonly category = \"calculator\" as const;\n\treadonly version = \"1.12\";\n\treadonly requiresAuth = true;\n\n\tprivate desmosProvider:\n\t\t| (CalculatorProvider & {\n\t\t\t\tinitialize(config: {\n\t\t\t\t\tapiKey?: string;\n\t\t\t\t\tproxyEndpoint?: string;\n\t\t\t\t\tonTelemetry?: (\n\t\t\t\t\t\teventName: string,\n\t\t\t\t\t\tpayload?: Record<string, unknown>,\n\t\t\t\t\t) => void | Promise<void>;\n\t\t\t\t}): Promise<void>;\n\t\t })\n\t\t| null = null;\n\tprivate config: DesmosToolProviderConfig | null = null;\n\n\tprivate async emitTelemetry(\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait this.config?.onTelemetry?.(eventName, payload);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[DesmosToolProvider] telemetry callback failed:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Initialize Desmos calculator provider\n\t *\n\t * Loads the Desmos API library and authenticates with provided credentials.\n\t *\n\t * @param config Configuration with API key or proxy endpoint\n\t * @throws Error if initialization fails\n\t */\n\tasync initialize(config: DesmosToolProviderConfig): Promise<void> {\n\t\tif (this.desmosProvider) {\n\t\t\tconsole.warn(\n\t\t\t\t\"[DesmosToolProvider] Already initialized, skipping reinitialization\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.config = config;\n\t\tconst moduleLoadStartedAt = Date.now();\n\t\tawait this.emitTelemetry(\"pie-tool-library-load-start\", {\n\t\t\ttoolId: \"calculator\",\n\t\t\toperation: \"desmos-provider-module-import\",\n\t\t\tbackend: \"desmos\",\n\t\t});\n\t\tconst desmosModule = await (async () => {\n\t\t\ttry {\n\t\t\t\tconst loaded = (await import(\"@pie-players/pie-calculator-desmos\")) as {\n\t\t\t\t\tDesmosCalculatorProvider: new () => CalculatorProvider & {\n\t\t\t\t\t\tinitialize(config: {\n\t\t\t\t\t\t\tapiKey?: string;\n\t\t\t\t\t\t\tproxyEndpoint?: string;\n\t\t\t\t\t\t\tonTelemetry?: (\n\t\t\t\t\t\t\t\teventName: string,\n\t\t\t\t\t\t\t\tpayload?: Record<string, unknown>,\n\t\t\t\t\t\t\t) => void | Promise<void>;\n\t\t\t\t\t\t}): Promise<void>;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-library-load-success\", {\n\t\t\t\t\ttoolId: \"calculator\",\n\t\t\t\t\toperation: \"desmos-provider-module-import\",\n\t\t\t\t\tbackend: \"desmos\",\n\t\t\t\t\tduration: Date.now() - moduleLoadStartedAt,\n\t\t\t\t});\n\t\t\t\treturn loaded;\n\t\t\t} catch (error) {\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-library-load-error\", {\n\t\t\t\t\ttoolId: \"calculator\",\n\t\t\t\t\toperation: \"desmos-provider-module-import\",\n\t\t\t\t\tbackend: \"desmos\",\n\t\t\t\t\tduration: Date.now() - moduleLoadStartedAt,\n\t\t\t\t\terrorType: \"ToolLibraryLoadError\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})();\n\t\tthis.desmosProvider = new desmosModule.DesmosCalculatorProvider();\n\n\t\t// Initialize with API key or proxy\n\t\ttry {\n\t\t\tawait this.desmosProvider.initialize({\n\t\t\t\tapiKey: config.apiKey,\n\t\t\t\tproxyEndpoint: config.proxyEndpoint,\n\t\t\t\tonTelemetry: config.onTelemetry,\n\t\t\t});\n\n\t\t\tconsole.log(\n\t\t\t\t`[DesmosToolProvider] Initialized successfully ${\n\t\t\t\t\tconfig.proxyEndpoint\n\t\t\t\t\t\t? \"(using proxy)\"\n\t\t\t\t\t\t: config.apiKey\n\t\t\t\t\t\t\t? \"(direct API key)\"\n\t\t\t\t\t\t\t: \"(no auth)\"\n\t\t\t\t}`,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"[DesmosToolProvider] Initialization failed:\", error);\n\t\t\tthrow new Error(\n\t\t\t\t\"Failed to initialize Desmos calculator provider. Check API key or proxy endpoint.\",\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Create a calculator provider instance\n\t *\n\t * Returns the initialized Desmos calculator provider.\n\t *\n\t * @param config Optional instance-specific configuration (currently unused)\n\t * @returns Desmos calculator provider\n\t * @throws Error if provider not initialized\n\t */\n\tasync createInstance(\n\t\tconfig?: Partial<DesmosToolProviderConfig>,\n\t): Promise<CalculatorProvider> {\n\t\tif (!this.desmosProvider) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[DesmosToolProvider] Provider not initialized. Call initialize() first.\",\n\t\t\t);\n\t\t}\n\n\t\treturn this.desmosProvider;\n\t}\n\n\t/**\n\t * Get provider capabilities\n\t *\n\t * @returns Desmos calculator capabilities\n\t */\n\tgetCapabilities(): ToolProviderCapabilities {\n\t\treturn {\n\t\t\tsupportsOffline: false, // Requires Desmos CDN\n\t\t\trequiresAuth: true,\n\t\t\tmaxInstances: null, // Unlimited calculator instances\n\t\t\tfeatures: {\n\t\t\t\tbasic: true,\n\t\t\t\tscientific: true,\n\t\t\t\tgraphing: true,\n\t\t\t\tfourFunction: true,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Check if provider is ready\n\t *\n\t * @returns true if provider is initialized\n\t */\n\tisReady(): boolean {\n\t\treturn this.desmosProvider !== null;\n\t}\n\n\t/**\n\t * Clean up provider resources\n\t *\n\t * Destroys the Desmos calculator provider and releases resources.\n\t */\n\tdestroy(): void {\n\t\tif (this.desmosProvider) {\n\t\t\tthis.desmosProvider.destroy();\n\t\t\tthis.desmosProvider = null;\n\t\t}\n\t\tthis.config = null;\n\t\tconsole.log(\"[DesmosToolProvider] Destroyed\");\n\t}\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"TTSToolProvider.js","sourceRoot":"","sources":["../../../src/services/tool-providers/TTSToolProvider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TTS Tool Provider\n *\n * Unified provider for all TTS backends:\n * - Browser Web Speech API (no auth)\n * - AWS Polly (requires auth via server proxy)\n * - Google Cloud TTS (requires auth via server proxy)\n *\n * Part of PIE Assessment Toolkit.\n */\n\nimport type { ITTSProvider, TTSConfig } from \"@pie-players/pie-tts\";\nimport { BrowserTTSProvider } from \"../../services/tts/browser-provider.js\";\nimport type {\n\tToolProviderApi,\n\tToolProviderCapabilities,\n} from \"./ToolProviderApi.js\";\n\n/**\n * TTS backend type\n */\nexport type TTSBackend = \"browser\" | \"polly\" | \"google\" | \"server\";\n\n/**\n * TTS tool provider configuration\n */\nexport interface TTSToolProviderConfig extends Partial<TTSConfig> {\n\t/**\n\t * TTS backend to use\n\t */\n\tbackend: TTSBackend;\n\n\t/**\n\t * Server API endpoint (for server backends)\n\t * @example '/api/tts/synthesize'\n\t * @example 'https://api.example.com/tts/synthesize'\n\t */\n\tapiEndpoint?: string;\n\n\t/**\n\t * Provider to use on server ('polly', 'google')\n\t * Only used when backend is 'server', 'polly', or 'google'\n\t */\n\tserverProvider?: \"polly\" | \"google\" | \"custom\";\n\n\t/**\n\t * Explicit transport mode for server provider payload translation.\n\t */\n\ttransportMode?: \"pie\" | \"custom\";\n\n\t/**\n\t * Endpoint style used by the server-backed provider.\n\t */\n\tendpointMode?: \"synthesizePath\" | \"rootPost\";\n\n\t/**\n\t * Endpoint validation strategy when validateEndpoint=true.\n\t */\n\tendpointValidationMode?: \"voices\" | \"endpoint\" | \"none\";\n\n\t/**\n\t * Include auth header when fetching remote audio/speech-mark assets.\n\t */\n\tincludeAuthOnAssetFetch?: boolean;\n\n\t/**\n\t * Auth token (if required)\n\t * Typically fetched via authFetcher in ToolProviderRegistry\n\t */\n\tauthToken?: string;\n\n\t/**\n\t * Organization ID for multi-tenant applications\n\t */\n\torganizationId?: string;\n\n\t/**\n\t * Default voice to use\n\t */\n\tvoice?: string;\n\n\t/**\n\t * Speech rate (0.25 to 4.0, default 1.0)\n\t */\n\trate?: number;\n\n\t/**\n\t * Speech pitch (0 to 2, default 1.0)\n\t * Note: Only browser backend supports pitch\n\t */\n\tpitch?: number;\n\n\t/**\n\t * Optional telemetry callback for tool/backend instrumentation.\n\t */\n\tonTelemetry?: (\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t) => void | Promise<void>;\n}\n\n/**\n * TTS Tool Provider\n *\n * Wraps TTS providers (Browser, Polly, Google) with the ToolProviderApi interface\n * for use in the ToolProviderRegistry.\n *\n * @example Browser TTS (no auth)\n * ```typescript\n * const provider = new TTSToolProvider();\n * await provider.initialize({ backend: 'browser' });\n * const ttsProvider = await provider.createInstance();\n * ```\n *\n * @example Server TTS (with auth)\n * ```typescript\n * const provider = new TTSToolProvider();\n * await provider.initialize({\n * backend: 'polly',\n * apiEndpoint: '/api/tts/synthesize',\n * authToken: 'bearer-token', // Fetched via authFetcher\n * });\n * const ttsProvider = await provider.createInstance();\n * ```\n */\nexport class TTSToolProvider\n\timplements ToolProviderApi<TTSToolProviderConfig, ITTSProvider>\n{\n\treadonly providerId = \"tts-service\";\n\treadonly providerName = \"Text-to-Speech\";\n\treadonly category = \"tts\" as const;\n\treadonly version = \"1.0\";\n\treadonly requiresAuth: boolean;\n\n\tprivate ttsProvider: ITTSProvider | null = null;\n\tprivate config: TTSToolProviderConfig | null = null;\n\n\tprivate async emitTelemetry(\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait this.config?.onTelemetry?.(eventName, payload);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[TTSToolProvider] telemetry callback failed:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Create TTS tool provider\n\t *\n\t * @param backend TTS backend to use (default: 'browser')\n\t */\n\tconstructor(backend: TTSBackend = \"browser\") {\n\t\tthis.requiresAuth = backend !== \"browser\";\n\t}\n\n\t/**\n\t * Initialize TTS provider\n\t *\n\t * Sets up the appropriate TTS backend.\n\t *\n\t * @param config Configuration with backend type and credentials\n\t * @throws Error if initialization fails or required config missing\n\t */\n\tasync initialize(config: TTSToolProviderConfig): Promise<void> {\n\t\tif (this.ttsProvider) {\n\t\t\tconsole.warn(\n\t\t\t\t\"[TTSToolProvider] Already initialized, skipping reinitialization\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.config = config;\n\n\t\tswitch (config.backend) {\n\t\t\tcase \"browser\":\n\t\t\t\tawait this._initializeBrowserTTS(config);\n\t\t\t\tbreak;\n\n\t\t\tcase \"polly\":\n\t\t\tcase \"google\":\n\t\t\tcase \"server\":\n\t\t\t\tawait this._initializeServerTTS(config);\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tthrow new Error(`[TTSToolProvider] Unknown backend: ${config.backend}`);\n\t\t}\n\n\t\tconsole.log(\n\t\t\t`[TTSToolProvider] Initialized successfully (backend: ${config.backend})`,\n\t\t);\n\t}\n\n\t/**\n\t * Initialize browser TTS (Web Speech API)\n\t */\n\tprivate async _initializeBrowserTTS(\n\t\tconfig: TTSToolProviderConfig,\n\t): Promise<void> {\n\t\t// Check if Web Speech API is available\n\t\tif (typeof window === \"undefined\" || !(\"speechSynthesis\" in window)) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[TTSToolProvider] Browser TTS not supported (Web Speech API not available)\",\n\t\t\t);\n\t\t}\n\n\t\tthis.ttsProvider = new BrowserTTSProvider();\n\t\tconsole.log(\"[TTSToolProvider] Browser TTS initialized (Web Speech API)\");\n\t}\n\n\t/**\n\t * Initialize server-based TTS (Polly, Google)\n\t */\n\tprivate async _initializeServerTTS(\n\t\tconfig: TTSToolProviderConfig,\n\t): Promise<void> {\n\t\tif (!config.apiEndpoint) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[TTSToolProvider] apiEndpoint required for server-based TTS backends\",\n\t\t\t);\n\t\t}\n\n\t\t// Server backends are optional; load implementation only when requested.\n\t\tconst moduleLoadStartedAt = Date.now();\n\t\tawait this.emitTelemetry(\"pie-tool-library-load-start\", {\n\t\t\ttoolId: \"textToSpeech\",\n\t\t\toperation: \"server-provider-module-import\",\n\t\t\tbackend: config.serverProvider || config.backend,\n\t\t});\n\t\tconst serverModule = await (async () => {\n\t\t\ttry {\n\t\t\t\tconst loaded = (await import(\"@pie-players/tts-client-server\")) as {\n\t\t\t\t\tServerTTSProvider: new () => ITTSProvider;\n\t\t\t\t};\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-library-load-success\", {\n\t\t\t\t\ttoolId: \"textToSpeech\",\n\t\t\t\t\toperation: \"server-provider-module-import\",\n\t\t\t\t\tbackend: config.serverProvider || config.backend,\n\t\t\t\t\tduration: Date.now() - moduleLoadStartedAt,\n\t\t\t\t});\n\t\t\t\treturn loaded;\n\t\t\t} catch (error) {\n\t\t\t\tawait this.emitTelemetry(\"pie-tool-library-load-error\", {\n\t\t\t\t\ttoolId: \"textToSpeech\",\n\t\t\t\t\toperation: \"server-provider-module-import\",\n\t\t\t\t\tbackend: config.serverProvider || config.backend,\n\t\t\t\t\tduration: Date.now() - moduleLoadStartedAt,\n\t\t\t\t\terrorType: \"ToolLibraryLoadError\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})();\n\t\tthis.ttsProvider = new serverModule.ServerTTSProvider();\n\n\t\tconsole.log(\n\t\t\t`[TTSToolProvider] Server TTS initialized (provider: ${config.serverProvider || config.backend})`,\n\t\t);\n\t}\n\n\t/**\n\t * Create a TTS provider instance\n\t *\n\t * Returns the initialized TTS provider.\n\t *\n\t * @param config Optional instance-specific configuration (currently unused)\n\t * @returns TTS provider\n\t * @throws Error if provider not initialized\n\t */\n\tasync createInstance(\n\t\tconfig?: Partial<TTSToolProviderConfig>,\n\t): Promise<ITTSProvider> {\n\t\tif (!this.ttsProvider) {\n\t\t\tthrow new Error(\n\t\t\t\t\"[TTSToolProvider] Provider not initialized. Call initialize() first.\",\n\t\t\t);\n\t\t}\n\n\t\treturn this.ttsProvider;\n\t}\n\n\t/**\n\t * Get provider capabilities\n\t *\n\t * @returns TTS capabilities based on backend\n\t */\n\tgetCapabilities(): ToolProviderCapabilities {\n\t\tconst isBrowser = this.config?.backend === \"browser\";\n\n\t\treturn {\n\t\t\tsupportsOffline: isBrowser,\n\t\t\trequiresAuth: !isBrowser,\n\t\t\tmaxInstances: 1, // Single TTS instance (playback is sequential)\n\t\t\tfeatures: {\n\t\t\t\twordBoundary: true, // All backends support word highlighting\n\t\t\t\tpause: true,\n\t\t\t\tresume: true,\n\t\t\t\trateControl: true,\n\t\t\t\tpitchControl: isBrowser, // Only browser supports pitch\n\t\t\t\tvoiceSelection: true,\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Check if provider is ready\n\t *\n\t * @returns true if provider is initialized\n\t */\n\tisReady(): boolean {\n\t\treturn this.ttsProvider !== null;\n\t}\n\n\t/**\n\t * Clean up provider resources\n\t *\n\t * Destroys the TTS provider and releases resources.\n\t */\n\tdestroy(): void {\n\t\tif (this.ttsProvider) {\n\t\t\tthis.ttsProvider.destroy();\n\t\t\tthis.ttsProvider = null;\n\t\t}\n\t\tthis.config = null;\n\t\tconsole.log(\"[TTSToolProvider] Destroyed\");\n\t}\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ToolProviderApi.js","sourceRoot":"","sources":["../../../src/services/tool-providers/ToolProviderApi.ts"],"names":[],"mappings":"AAAA;;;;;GAKG","sourcesContent":["/**\n * Tool Provider API\n *\n * Unified interface for all assessment tools that require\n * configuration, authentication, or external services.\n */\n\nexport type ToolCategory =\n\t| \"calculator\"\n\t| \"tts\"\n\t| \"translation\"\n\t| \"annotation\"\n\t| \"accessibility\"\n\t| \"other\";\n\nexport interface ToolProviderCapabilities {\n\tsupportsOffline: boolean;\n\trequiresAuth: boolean;\n\tmaxInstances?: number | null;\n\tfeatures: Record<string, boolean>;\n}\n\nexport interface ToolProviderApi<TConfig = any, TInstance = any> {\n\treadonly providerId: string;\n\treadonly providerName: string;\n\treadonly category: ToolCategory;\n\treadonly version: string;\n\treadonly requiresAuth: boolean;\n\tinitialize(config: TConfig): Promise<void>;\n\tcreateInstance(config?: Partial<TConfig>): Promise<TInstance>;\n\tgetCapabilities(): ToolProviderCapabilities;\n\tisReady(): boolean;\n\tdestroy(): void;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ToolProviderRegistry.js","sourceRoot":"","sources":["../../../src/services/tool-providers/ToolProviderRegistry.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Tool Provider Registry\n *\n * Centralized registry for managing tool providers.\n * Handles initialization, authentication, and lazy loading.\n *\n * Part of PIE Assessment Toolkit.\n */\n\nimport type { ToolProviderApi, ToolCategory } from \"./ToolProviderApi.js\";\n\n/**\n * Configuration for registering a tool provider\n */\nexport interface ToolProviderConfig<TConfig = any> {\n\t/**\n\t * Provider instance to register\n\t */\n\tprovider: ToolProviderApi<TConfig>;\n\n\t/**\n\t * Configuration for this provider\n\t * (may include auth credentials, endpoints, etc.)\n\t */\n\tconfig: TConfig;\n\n\t/**\n\t * Lazy initialization - don't initialize until first use\n\t *\n\t * @default true\n\t * @note Set to false for providers that must be ready immediately\n\t */\n\tlazy?: boolean;\n\n\t/**\n\t * Auth fetcher - function to retrieve auth credentials from backend\n\t *\n\t * Called during initialization if the provider requires auth.\n\t * The returned data is merged with the config before initialization.\n\t *\n\t * @example\n\t * ```typescript\n\t * authFetcher: async () => {\n\t * const response = await fetch('/api/desmos/token');\n\t * return response.json(); // { apiKey: '...' }\n\t * }\n\t * ```\n\t */\n\tauthFetcher?: () => Promise<Partial<TConfig>>;\n\n\t/**\n\t * Optional telemetry callback used for provider lifecycle/backend instrumentation.\n\t */\n\tonTelemetry?: (\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t) => void | Promise<void>;\n}\n\n/**\n * Tool Provider Registry\n *\n * Manages the lifecycle of tool providers:\n * - Registration\n * - Lazy initialization\n * - Auth credential fetching\n * - Provider lookup by ID or category\n *\n * @example\n * ```typescript\n * const registry = new ToolProviderRegistry();\n *\n * // Register a provider with auth fetcher\n * registry.register('desmos-calculator', {\n * provider: new DesmosToolProvider(),\n * config: {},\n * lazy: true,\n * authFetcher: async () => {\n * const response = await fetch('/api/desmos/token');\n * return response.json();\n * },\n * });\n *\n * // Get provider (auto-initializes if lazy)\n * const desmosProvider = await registry.getProvider('desmos-calculator');\n * ```\n */\nexport class ToolProviderRegistry {\n\tprivate providers = new Map<string, ToolProviderApi>();\n\tprivate configs = new Map<string, ToolProviderConfig>();\n\tprivate initialized = new Map<string, boolean>();\n\tprivate initializationPromises = new Map<string, Promise<void>>();\n\n\tprivate async emitTelemetry(\n\t\tconfig: ToolProviderConfig,\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tawait config.onTelemetry?.(eventName, payload);\n\t\t} catch (error) {\n\t\t\tconsole.warn(\"[ToolProviderRegistry] Telemetry callback failed:\", error);\n\t\t}\n\t}\n\n\t/**\n\t * Register a tool provider\n\t *\n\t * Adds a provider to the registry. If lazy is false, initializes immediately.\n\t *\n\t * @param providerId Unique provider identifier\n\t * @param config Provider configuration\n\t * @throws Error if provider with same ID already registered\n\t */\n\tregister(providerId: string, config: ToolProviderConfig): void {\n\t\tif (this.providers.has(providerId)) {\n\t\t\tconsole.warn(\n\t\t\t\t`[ToolProviderRegistry] Provider \"${providerId}\" already registered, skipping`,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.providers.set(providerId, config.provider);\n\t\tthis.configs.set(providerId, config);\n\t\tthis.initialized.set(providerId, false);\n\n\t\tconsole.log(\n\t\t\t`[ToolProviderRegistry] Registered provider \"${providerId}\" (${config.provider.providerName})`,\n\t\t);\n\n\t\t// Initialize immediately if not lazy\n\t\tif (config.lazy === false) {\n\t\t\tthis.initialize(providerId).catch((error) => {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[ToolProviderRegistry] Failed to initialize provider \"${providerId}\":`,\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Initialize a provider\n\t *\n\t * Fetches auth if needed and initializes the provider.\n\t * Safe to call multiple times - subsequent calls wait for first initialization.\n\t *\n\t * @param providerId Provider to initialize\n\t * @returns Promise that resolves when initialization complete\n\t * @throws Error if provider not registered or initialization fails\n\t */\n\tasync initialize(providerId: string): Promise<void> {\n\t\t// Already initialized\n\t\tif (this.initialized.get(providerId)) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Initialization in progress - wait for it\n\t\tconst existingPromise = this.initializationPromises.get(providerId);\n\t\tif (existingPromise) {\n\t\t\treturn existingPromise;\n\t\t}\n\n\t\t// Start new initialization\n\t\tconst initPromise = this._doInitialize(providerId);\n\t\tthis.initializationPromises.set(providerId, initPromise);\n\n\t\ttry {\n\t\t\tawait initPromise;\n\t\t} finally {\n\t\t\tthis.initializationPromises.delete(providerId);\n\t\t}\n\t}\n\n\t/**\n\t * Internal initialization logic\n\t */\n\tprivate async _doInitialize(providerId: string): Promise<void> {\n\t\tconst provider = this.providers.get(providerId);\n\t\tconst config = this.configs.get(providerId);\n\n\t\tif (!provider || !config) {\n\t\t\tthrow new Error(\n\t\t\t\t`[ToolProviderRegistry] Provider \"${providerId}\" not registered`,\n\t\t\t);\n\t\t}\n\n\t\t// Start with base config\n\t\tlet providerConfig = { ...config.config };\n\t\tconst deriveBackend = (value: unknown): string => {\n\t\t\tif (!value || typeof value !== \"object\") return \"unknown\";\n\t\t\tconst typed = value as Record<string, unknown>;\n\t\t\tconst direct =\n\t\t\t\ttypeof typed.backend === \"string\"\n\t\t\t\t\t? typed.backend\n\t\t\t\t\t: typeof typed.provider === \"string\"\n\t\t\t\t\t\t? typed.provider\n\t\t\t\t\t\t: typeof typed.serverProvider === \"string\"\n\t\t\t\t\t\t\t? typed.serverProvider\n\t\t\t\t\t\t\t: typeof typed.providerId === \"string\"\n\t\t\t\t\t\t\t\t? typed.providerId\n\t\t\t\t\t\t\t\t: \"\";\n\t\t\treturn direct || \"unknown\";\n\t\t};\n\t\tconst providerInitStartedAt = Date.now();\n\t\tawait this.emitTelemetry(config, \"pie-tool-init-start\", {\n\t\t\ttoolId: providerId,\n\t\t\tproviderId,\n\t\t\tbackend: deriveBackend(providerConfig),\n\t\t\toperation: \"provider-initialize\",\n\t\t});\n\n\t\t// Fetch auth if needed\n\t\tif (provider.requiresAuth && config.authFetcher) {\n\t\t\tconst authFetchStartedAt = Date.now();\n\t\t\tawait this.emitTelemetry(config, \"pie-tool-backend-call-start\", {\n\t\t\t\ttoolId: providerId,\n\t\t\t\tproviderId,\n\t\t\t\tbackend: deriveBackend(providerConfig),\n\t\t\t\toperation: \"auth-fetch\",\n\t\t\t});\n\t\t\tconsole.log(\n\t\t\t\t`[ToolProviderRegistry] Fetching auth for \"${providerId}\"...`,\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tconst authData = await config.authFetcher();\n\t\t\t\tproviderConfig = { ...providerConfig, ...authData };\n\t\t\t\tawait this.emitTelemetry(config, \"pie-tool-backend-call-success\", {\n\t\t\t\t\ttoolId: providerId,\n\t\t\t\t\tproviderId,\n\t\t\t\t\tbackend: deriveBackend(providerConfig),\n\t\t\t\t\toperation: \"auth-fetch\",\n\t\t\t\t\tduration: Date.now() - authFetchStartedAt,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tawait this.emitTelemetry(config, \"pie-tool-backend-call-error\", {\n\t\t\t\t\ttoolId: providerId,\n\t\t\t\t\tproviderId,\n\t\t\t\t\tbackend: deriveBackend(providerConfig),\n\t\t\t\t\toperation: \"auth-fetch\",\n\t\t\t\t\tduration: Date.now() - authFetchStartedAt,\n\t\t\t\t\terrorType: \"ProviderAuthFetchError\",\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[ToolProviderRegistry] Auth fetch failed for \"${providerId}\":`,\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Failed to fetch auth credentials for provider \"${providerId}\"`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Initialize provider\n\t\ttry {\n\t\t\tawait provider.initialize(providerConfig);\n\t\t\tthis.initialized.set(providerId, true);\n\t\t\tawait this.emitTelemetry(config, \"pie-tool-init-success\", {\n\t\t\t\ttoolId: providerId,\n\t\t\t\tproviderId,\n\t\t\t\tbackend: deriveBackend(providerConfig),\n\t\t\t\toperation: \"provider-initialize\",\n\t\t\t\tduration: Date.now() - providerInitStartedAt,\n\t\t\t});\n\t\t\tconsole.log(\n\t\t\t\t`[ToolProviderRegistry] Provider \"${providerId}\" initialized`,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tawait this.emitTelemetry(config, \"pie-tool-init-error\", {\n\t\t\t\ttoolId: providerId,\n\t\t\t\tproviderId,\n\t\t\t\tbackend: deriveBackend(providerConfig),\n\t\t\t\toperation: \"provider-initialize\",\n\t\t\t\tduration: Date.now() - providerInitStartedAt,\n\t\t\t\terrorType: \"ProviderInitializationError\",\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t\tconsole.error(\n\t\t\t\t`[ToolProviderRegistry] Initialization failed for \"${providerId}\":`,\n\t\t\t\terror,\n\t\t\t);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Get a provider instance\n\t *\n\t * Retrieves a registered provider. If autoInitialize is true and provider\n\t * is not initialized, initializes it first.\n\t *\n\t * @param providerId Provider identifier\n\t * @param autoInitialize Auto-initialize if not ready (default: true)\n\t * @returns Provider instance\n\t * @throws Error if provider not registered\n\t */\n\tasync getProvider<T extends ToolProviderApi = ToolProviderApi>(\n\t\tproviderId: string,\n\t\tautoInitialize = true,\n\t): Promise<T> {\n\t\tconsole.log(\"[ToolProviderRegistry] getProvider called\", {\n\t\t\tproviderId,\n\t\t\tautoInitialize,\n\t\t\tisRegistered: this.providers.has(providerId),\n\t\t\tisInitialized: this.initialized.get(providerId),\n\t\t\tallProviderIds: Array.from(this.providers.keys()),\n\t\t});\n\n\t\tconst provider = this.providers.get(providerId);\n\t\tif (!provider) {\n\t\t\tconsole.error(\"[ToolProviderRegistry] Provider not found:\", {\n\t\t\t\trequestedId: providerId,\n\t\t\t\tavailableProviders: Array.from(this.providers.keys()),\n\t\t\t});\n\t\t\tthrow new Error(\n\t\t\t\t`[ToolProviderRegistry] Provider \"${providerId}\" not registered`,\n\t\t\t);\n\t\t}\n\n\t\t// Auto-initialize if needed\n\t\tif (autoInitialize && !this.initialized.get(providerId)) {\n\t\t\tconsole.log(\n\t\t\t\t\"[ToolProviderRegistry] Auto-initializing provider:\",\n\t\t\t\tproviderId,\n\t\t\t);\n\t\t\tawait this.initialize(providerId);\n\t\t}\n\n\t\tconsole.log(\"[ToolProviderRegistry] Returning provider:\", {\n\t\t\tproviderId,\n\t\t\tproviderName: provider.providerName,\n\t\t\tisReady: provider.isReady(),\n\t\t});\n\n\t\treturn provider as T;\n\t}\n\n\t/**\n\t * Get all providers in a category\n\t *\n\t * @param category Tool category to filter by\n\t * @returns Array of provider IDs in the category\n\t */\n\tgetProvidersByCategory(category: ToolCategory): string[] {\n\t\treturn Array.from(this.providers.entries())\n\t\t\t.filter(([_, provider]) => provider.category === category)\n\t\t\t.map(([id]) => id);\n\t}\n\n\t/**\n\t * Get all registered provider IDs\n\t *\n\t * @returns Array of all provider IDs\n\t */\n\tgetProviderIds(): string[] {\n\t\treturn Array.from(this.providers.keys());\n\t}\n\n\t/**\n\t * Check if provider is registered\n\t *\n\t * @param providerId Provider identifier\n\t * @returns true if provider is registered\n\t */\n\thas(providerId: string): boolean {\n\t\treturn this.providers.has(providerId);\n\t}\n\n\t/**\n\t * Check if provider is initialized\n\t *\n\t * @param providerId Provider identifier\n\t * @returns true if provider is initialized\n\t */\n\tisInitialized(providerId: string): boolean {\n\t\treturn this.initialized.get(providerId) === true;\n\t}\n\n\t/**\n\t * Check if provider is currently initializing\n\t *\n\t * @param providerId Provider identifier\n\t * @returns true if initialization in progress\n\t */\n\tisInitializing(providerId: string): boolean {\n\t\treturn this.initializationPromises.has(providerId);\n\t}\n\n\t/**\n\t * Unregister and destroy a provider\n\t *\n\t * Removes the provider from the registry and calls its destroy method.\n\t *\n\t * @param providerId Provider to unregister\n\t */\n\tasync unregister(providerId: string): Promise<void> {\n\t\tconst provider = this.providers.get(providerId);\n\t\tif (provider) {\n\t\t\t// Wait for any pending initialization\n\t\t\tconst initPromise = this.initializationPromises.get(providerId);\n\t\t\tif (initPromise) {\n\t\t\t\ttry {\n\t\t\t\t\tawait initPromise;\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore initialization errors during unregister\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Destroy provider\n\t\t\tprovider.destroy();\n\n\t\t\t// Remove from registry\n\t\t\tthis.providers.delete(providerId);\n\t\t\tthis.configs.delete(providerId);\n\t\t\tthis.initialized.delete(providerId);\n\t\t\tthis.initializationPromises.delete(providerId);\n\n\t\t\tconsole.log(\n\t\t\t\t`[ToolProviderRegistry] Unregistered provider \"${providerId}\"`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Clean up all providers\n\t *\n\t * Unregisters and destroys all providers in the registry.\n\t */\n\tasync destroy(): Promise<void> {\n\t\tconst providerIds = Array.from(this.providers.keys());\n\t\tawait Promise.all(providerIds.map((id) => this.unregister(id)));\n\t\tconsole.log(\"[ToolProviderRegistry] Registry destroyed\");\n\t}\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/tool-providers/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,WAAW;AACX,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAGjE,qBAAqB;AACrB,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAG7D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["/**\n * Tool Provider System\n *\n * Unified framework for managing assessment tools with authentication,\n * configuration, and lifecycle management.\n *\n * Part of PIE Assessment Toolkit.\n */\n\n// Core interfaces\nexport type {\n\tToolProviderApi,\n\tToolCategory,\n\tToolProviderCapabilities,\n} from \"./ToolProviderApi.js\";\n\n// Registry\nexport { ToolProviderRegistry } from \"./ToolProviderRegistry.js\";\nexport type { ToolProviderConfig } from \"./ToolProviderRegistry.js\";\n\n// Concrete providers\nexport { DesmosToolProvider } from \"./DesmosToolProvider.js\";\nexport type { DesmosToolProviderConfig } from \"./DesmosToolProvider.js\";\n\nexport { TTSToolProvider } from \"./TTSToolProvider.js\";\nexport type {\n\tTTSToolProviderConfig,\n\tTTSBackend,\n} from \"./TTSToolProvider.js\";\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"toolbar-items.js","sourceRoot":"","sources":["../../src/services/toolbar-items.ts"],"names":[],"mappings":"AA0BA,MAAM,UAAU,iBAAiB,CAAC,IAAiB;IAClD,OAAO,OAAQ,IAAwB,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,KAAc;IACrD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9E,MAAM,IAAI,GAAG,KAAgC,CAAC;IAC9C,IAAI,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC7E,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QACnE,OAAO,KAAK,CAAC;IACd,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ;QACrE,OAAO,KAAK,CAAC;IACd,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC3E,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ;QACjE,OAAO,KAAK,CAAC;IACd,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,SAAS;QAChE,OAAO,KAAK,CAAC;IACd,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,SAAS;QACpE,OAAO,KAAK,CAAC;IAEd,MAAM,OAAO,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC;IAC9C,MAAM,UAAU,GAAG,OAAO,IAAI,CAAC,OAAO,KAAK,UAAU,CAAC;IACtD,IAAI,OAAO,KAAK,UAAU;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,OAAO;QAAE,OAAO,IAAI,CAAC;IACzB,OAAO,UAAU,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAwB;IACvD,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAwB;IACzD,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AACxE,CAAC","sourcesContent":["export interface ToolbarItemBase {\n\tid: string;\n\tlabel: string;\n\tariaLabel?: string;\n\ticon?: string;\n\ttooltip?: string;\n\tactive?: boolean;\n\tdisabled?: boolean;\n}\n\nexport interface ToolbarButtonItem extends ToolbarItemBase {\n\tonClick: () => void;\n\thref?: never;\n\ttarget?: never;\n\trel?: never;\n}\n\nexport interface ToolbarLinkItem extends ToolbarItemBase {\n\thref: string;\n\ttarget?: \"_self\" | \"_blank\" | \"_parent\" | \"_top\" | string;\n\trel?: string;\n\tonClick?: never;\n}\n\nexport type ToolbarItem = ToolbarButtonItem | ToolbarLinkItem;\n\nexport function isToolbarLinkItem(item: ToolbarItem): item is ToolbarLinkItem {\n\treturn typeof (item as ToolbarLinkItem).href === \"string\";\n}\n\nexport function isValidToolbarItemShape(value: unknown): value is ToolbarItem {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) return false;\n\tconst item = value as Record<string, unknown>;\n\tif (typeof item.id !== \"string\" || item.id.trim().length === 0) return false;\n\tif (typeof item.label !== \"string\" || item.label.trim().length === 0)\n\t\treturn false;\n\tif (item.ariaLabel !== undefined && typeof item.ariaLabel !== \"string\")\n\t\treturn false;\n\tif (item.icon !== undefined && typeof item.icon !== \"string\") return false;\n\tif (item.tooltip !== undefined && typeof item.tooltip !== \"string\")\n\t\treturn false;\n\tif (item.active !== undefined && typeof item.active !== \"boolean\")\n\t\treturn false;\n\tif (item.disabled !== undefined && typeof item.disabled !== \"boolean\")\n\t\treturn false;\n\n\tconst hasHref = typeof item.href === \"string\";\n\tconst hasOnClick = typeof item.onClick === \"function\";\n\tif (hasHref === hasOnClick) return false;\n\tif (hasHref) return true;\n\treturn hasOnClick;\n}\n\nexport function isInlineSvgIcon(icon: string | undefined): boolean {\n\treturn typeof icon === \"string\" && icon.trimStart().startsWith(\"<svg\");\n}\n\nexport function isExternalIconUrl(icon: string | undefined): boolean {\n\treturn typeof icon === \"string\" && icon.trimStart().startsWith(\"http\");\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tools-config-normalizer.js","sourceRoot":"","sources":["../../src/services/tools-config-normalizer.ts"],"names":[],"mappings":"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","sourcesContent":["import { DEFAULT_TOOL_PLACEMENT } from \"./tool-config-defaults.js\";\nimport type { SREMathSpeechOptions } from \"./tts/math-speech.js\";\n\nexport type ToolPlacementLevel = \"section\" | \"item\" | \"passage\";\n\nexport interface ToolPolicyConfig {\n\tallowed?: string[];\n\tblocked?: string[];\n}\n\nexport interface ToolPlacementConfig {\n\tsection?: string[];\n\titem?: string[];\n\tpassage?: string[];\n}\n\nexport interface ToolRuntimeBackendRequest {\n\tpath: string;\n\tmethod?: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\theaders?: Record<string, string>;\n\tquery?: Record<string, string | number | boolean>;\n\tbody?: unknown;\n}\n\nexport interface ToolRuntimeProviderBridge {\n\t/**\n\t * Optional auth fetch hook for provider initialization.\n\t * Useful for retrieving temporary credentials from a host backend.\n\t */\n\tauthFetcher?: () => Promise<Record<string, unknown>>;\n\t/**\n\t * Optional backend request bridge for rich tool interactions\n\t * beyond one-time auth fetching.\n\t */\n\trequest?: (request: ToolRuntimeBackendRequest) => Promise<unknown>;\n\t/**\n\t * Optional host event emitter.\n\t */\n\temit?: (\n\t\teventName: string,\n\t\tpayload?: Record<string, unknown>,\n\t) => void | Promise<void>;\n\t/**\n\t * Optional host event subscription bridge.\n\t */\n\tsubscribe?: (\n\t\teventName: string,\n\t\thandler: (payload: unknown) => void,\n\t) => (() => void) | undefined;\n\t[key: string]: unknown;\n}\n\nexport interface ToolRuntimeProviderConfig {\n\tid?: string;\n\tinit?: Record<string, unknown>;\n\truntime?: ToolRuntimeProviderBridge;\n}\n\nexport type TextToSpeechRuntimeProviderId = \"polly\" | \"google\" | \"custom\";\n\nexport interface ToolProviderConfig {\n\tenabled?: boolean;\n\tprovider?: ToolRuntimeProviderConfig;\n\tsettings?: Record<string, unknown>;\n\t[key: string]: unknown;\n}\n\nexport interface TextToSpeechToolProviderSettings\n\textends Record<string, unknown> {\n\tmathSpeech?: SREMathSpeechOptions;\n}\n\nexport interface TextToSpeechToolProviderConfig\n\textends Omit<ToolProviderConfig, \"provider\" | \"settings\"> {\n\tprovider?: ToolRuntimeProviderConfig | TextToSpeechRuntimeProviderId;\n\tserverProvider?: TextToSpeechRuntimeProviderId;\n\tmathSpeech?: SREMathSpeechOptions;\n\tsettings?: TextToSpeechToolProviderSettings;\n}\n\nexport interface ToolProvidersConfig {\n\ttextToSpeech?: TextToSpeechToolProviderConfig;\n\t[key: string]:\n\t\t| ToolProviderConfig\n\t\t| TextToSpeechToolProviderConfig\n\t\t| undefined;\n}\n\n/**\n * PNP/profile enforcement mode mirrored on `runtime.tools.pnpEnforcement`\n * (M5 mirror rule). Carried alongside `policy` / `placement` /\n * `providers` so hosts can pin or opt out of PNP/profile precedence via\n * the runtime config without reaching for the toolkit prop directly.\n *\n * - `\"on\"` — force enforcement (engine applies PNP/profile gates).\n * - `\"off\"` — opt out (engine ignores PNP/profile inputs).\n * - omitted / `undefined` — auto-mode (the default). The toolkit\n * coordinator computes the effective mode from PNP/profile policy material on the\n * bound `AssessmentEntity` / `AssessmentItemRef`. See\n * `resolveDefaultPnpEnforcement` in `policy/core/pnp-policy-inputs.ts`.\n */\nexport type ToolsPnpEnforcement = \"on\" | \"off\";\n\nexport interface CanonicalToolsConfig {\n\tpolicy: ToolPolicyConfig;\n\tplacement: Required<ToolPlacementConfig>;\n\tproviders: ToolProvidersConfig;\n\t/**\n\t * Optional PNP/profile enforcement override. Mirrored on\n\t * `runtime.tools.pnpEnforcement`; consumed by\n\t * `<pie-assessment-toolkit>` and `ToolkitCoordinator` as the\n\t * embedded path's equivalent of the standalone `pnp-enforcement`\n\t * attribute. See {@link ToolsPnpEnforcement} for semantics.\n\t */\n\tpnpEnforcement?: ToolsPnpEnforcement;\n}\n\nconst DEFAULT_PLACEMENT: Required<ToolPlacementConfig> = {\n\tsection: [...DEFAULT_TOOL_PLACEMENT.section],\n\titem: [...DEFAULT_TOOL_PLACEMENT.item],\n\tpassage: [...DEFAULT_TOOL_PLACEMENT.passage],\n};\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction assertStringArray(value: unknown, fieldPath: string): string[] {\n\tif (value == null) return [];\n\tif (!Array.isArray(value)) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"${fieldPath}\": expected an array of tool ids.`,\n\t\t);\n\t}\n\tconst invalidEntry = value.find((entry) => typeof entry !== \"string\");\n\tif (invalidEntry !== undefined) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"${fieldPath}\": all entries must be strings.`,\n\t\t);\n\t}\n\treturn value as string[];\n}\n\nfunction assertPlacementConfig(\n\tvalue: unknown,\n): ToolPlacementConfig | undefined {\n\tif (value == null) return undefined;\n\tif (!isPlainObject(value)) {\n\t\tthrow new Error(\n\t\t\t'Invalid tools config at \"placement\": expected an object with section/item/passage arrays.',\n\t\t);\n\t}\n\treturn value as ToolPlacementConfig;\n}\n\nfunction assertPolicyConfig(value: unknown): ToolPolicyConfig | undefined {\n\tif (value == null) return undefined;\n\tif (!isPlainObject(value)) {\n\t\tthrow new Error(\n\t\t\t'Invalid tools config at \"policy\": expected an object with allowed/blocked arrays.',\n\t\t);\n\t}\n\treturn value as ToolPolicyConfig;\n}\n\nfunction assertProviderConfig(\n\tproviderId: string,\n\tvalue: unknown,\n): ToolProviderConfig | TextToSpeechToolProviderConfig | undefined {\n\tif (value == null) return undefined;\n\tif (!isPlainObject(value)) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"providers.${providerId}\": expected an object.`,\n\t\t);\n\t}\n\tconst config = value as ToolProviderConfig;\n\tconst isTTSRuntimeProviderId = (provider: unknown): boolean =>\n\t\tprovider === \"polly\" || provider === \"google\" || provider === \"custom\";\n\tif (\n\t\t\"enabled\" in config &&\n\t\tconfig.enabled !== undefined &&\n\t\ttypeof config.enabled !== \"boolean\"\n\t) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"providers.${providerId}.enabled\": expected a boolean.`,\n\t\t);\n\t}\n\tif (\n\t\t\"settings\" in config &&\n\t\tconfig.settings !== undefined &&\n\t\t!isPlainObject(config.settings)\n\t) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"providers.${providerId}.settings\": expected an object.`,\n\t\t);\n\t}\n\tif (\n\t\t\"provider\" in config &&\n\t\tconfig.provider !== undefined &&\n\t\t!isPlainObject(config.provider) &&\n\t\t!(providerId === \"textToSpeech\" && isTTSRuntimeProviderId(config.provider))\n\t) {\n\t\tthrow new Error(\n\t\t\t`Invalid tools config at \"providers.${providerId}.provider\": expected an object.`,\n\t\t);\n\t}\n\treturn config;\n}\n\nfunction assertProvidersConfig(value: unknown): ToolProvidersConfig {\n\tif (value == null) return {};\n\tif (!isPlainObject(value)) {\n\t\tthrow new Error(\n\t\t\t'Invalid tools config at \"providers\": expected an object keyed by tool id.',\n\t\t);\n\t}\n\tconst normalized: ToolProvidersConfig = {};\n\tfor (const [providerId, providerConfig] of Object.entries(value)) {\n\t\tnormalized[providerId] = assertProviderConfig(providerId, providerConfig);\n\t}\n\treturn normalized;\n}\n\nexport function normalizeToolAlias(toolId: string): string {\n\tconst trimmed = toolId.trim();\n\tif (!trimmed) return \"\";\n\treturn trimmed;\n}\n\nexport function normalizeToolList(\n\ttoolIds: string[] | undefined | null,\n): string[] {\n\tif (!toolIds || toolIds.length === 0) return [];\n\tconst deduped = new Set<string>();\n\tfor (const rawToolId of toolIds) {\n\t\tif (typeof rawToolId !== \"string\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"Invalid tools config: tool list entries must be strings.\",\n\t\t\t);\n\t\t}\n\t\tconst normalized = normalizeToolAlias(rawToolId);\n\t\tif (!normalized) continue;\n\t\tdeduped.add(normalized);\n\t}\n\treturn Array.from(deduped);\n}\n\nexport function parseToolList(input: string | undefined | null): string[] {\n\tif (!input) return [];\n\treturn normalizeToolList(\n\t\tinput\n\t\t\t.split(\",\")\n\t\t\t.map((entry) => entry.trim())\n\t\t\t.filter(Boolean),\n\t);\n}\n\nfunction assertPnpEnforcement(\n\tvalue: unknown,\n\tfieldPath: \"pnpEnforcement\",\n): ToolsPnpEnforcement | undefined {\n\tif (value == null) return undefined;\n\tif (value === \"on\" || value === \"off\") return value;\n\tthrow new Error(\n\t\t`Invalid tools config at \"${fieldPath}\": expected \"on\" or \"off\", got ${JSON.stringify(value)}.`,\n\t);\n}\n\nexport function normalizeToolsConfig(\n\tinput?: Partial<CanonicalToolsConfig> | null,\n): CanonicalToolsConfig {\n\tif (input != null && !isPlainObject(input)) {\n\t\tthrow new Error(\n\t\t\t'Invalid tools config: expected an object with \"policy\", \"placement\", and \"providers\".',\n\t\t);\n\t}\n\tconst policy = assertPolicyConfig(input?.policy);\n\tconst placement = assertPlacementConfig(input?.placement);\n\tconst providers = assertProvidersConfig(input?.providers);\n\tconst pnpEnforcement = assertPnpEnforcement(\n\t\tinput?.pnpEnforcement,\n\t\t\"pnpEnforcement\",\n\t);\n\n\tconst config: CanonicalToolsConfig = {\n\t\tpolicy: {\n\t\t\tallowed: normalizeToolList(\n\t\t\t\tassertStringArray(policy?.allowed, \"policy.allowed\"),\n\t\t\t),\n\t\t\tblocked: normalizeToolList(\n\t\t\t\tassertStringArray(policy?.blocked, \"policy.blocked\"),\n\t\t\t),\n\t\t},\n\t\tplacement: {\n\t\t\tsection: normalizeToolList(\n\t\t\t\tassertStringArray(placement?.section, \"placement.section\"),\n\t\t\t).length\n\t\t\t\t? normalizeToolList(\n\t\t\t\t\t\tassertStringArray(placement?.section, \"placement.section\"),\n\t\t\t\t\t)\n\t\t\t\t: [...DEFAULT_PLACEMENT.section],\n\t\t\titem: normalizeToolList(\n\t\t\t\tassertStringArray(placement?.item, \"placement.item\"),\n\t\t\t).length\n\t\t\t\t? normalizeToolList(\n\t\t\t\t\t\tassertStringArray(placement?.item, \"placement.item\"),\n\t\t\t\t\t)\n\t\t\t\t: [...DEFAULT_PLACEMENT.item],\n\t\t\tpassage: normalizeToolList(\n\t\t\t\tassertStringArray(placement?.passage, \"placement.passage\"),\n\t\t\t).length\n\t\t\t\t? normalizeToolList(\n\t\t\t\t\t\tassertStringArray(placement?.passage, \"placement.passage\"),\n\t\t\t\t\t)\n\t\t\t\t: [...DEFAULT_PLACEMENT.passage],\n\t\t},\n\t\tproviders,\n\t};\n\tif (pnpEnforcement) {\n\t\tconfig.pnpEnforcement = pnpEnforcement;\n\t}\n\treturn config;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"browser-provider.js","sourceRoot":"","sources":["../../../src/services/tts/browser-provider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Browser TTS Provider\n *\n * Text-to-speech provider using the browser's Web Speech API.\n * Works in all modern browsers that support SpeechSynthesis.\n *\n * Part of PIE Assessment Toolkit.\n */\n\nimport type {\n\tITTSProvider,\n\tITTSProviderImplementation,\n\tTTSConfig,\n\tTTSFeature,\n\tTTSProviderCapabilities,\n} from \"@pie-players/pie-tts\";\nimport { segmentSentences as segmentTextToSentences } from \"./text-segmentation.js\";\n\ninterface TTSSpeechSegment {\n\ttext: string;\n\tstartOffset: number;\n\tpauseMsAfter?: number;\n}\n\nconst normalizeLanguageCode = (value: unknown): string =>\n\tString(value || \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\nconst browserLanguage = (): string => {\n\tconst navigatorLanguage =\n\t\ttypeof navigator !== \"undefined\"\n\t\t\t? navigator.language || navigator.languages?.[0]\n\t\t\t: \"\";\n\treturn normalizeLanguageCode(navigatorLanguage || \"en-US\");\n};\n\nconst findBrowserVoice = (\n\tvoices: SpeechSynthesisVoice[],\n\tpreferredName?: string,\n): SpeechSynthesisVoice | null => {\n\tif (preferredName) {\n\t\tconst explicit = voices.find((voice) => voice.name === preferredName);\n\t\tif (explicit) return explicit;\n\t}\n\tconst language = browserLanguage();\n\tconst languagePrefix = language.split(\"-\")[0] || \"en\";\n\tconst matchesLanguage = (voice: SpeechSynthesisVoice) => {\n\t\tconst voiceLanguage = normalizeLanguageCode(voice.lang);\n\t\treturn voiceLanguage === language || voiceLanguage.startsWith(`${languagePrefix}-`);\n\t};\n\tconst ranked = [\n\t\t(voice: SpeechSynthesisVoice) => voice.localService && matchesLanguage(voice),\n\t\t(voice: SpeechSynthesisVoice) => voice.default && matchesLanguage(voice),\n\t\t(voice: SpeechSynthesisVoice) => matchesLanguage(voice),\n\t\t(voice: SpeechSynthesisVoice) => voice.localService,\n\t\t(voice: SpeechSynthesisVoice) => voice.default,\n\t];\n\tfor (const predicate of ranked) {\n\t\tconst voice = voices.find(predicate);\n\t\tif (voice) return voice;\n\t}\n\treturn voices[0] || null;\n};\n\nconst shouldAssignBrowserVoice = (voice: SpeechSynthesisVoice): boolean => !voice.default;\n\n/**\n * Browser TTS Provider\n *\n * Stateless factory for creating browser-based TTS implementations.\n */\nexport class BrowserTTSProvider implements ITTSProvider {\n\treadonly providerId = \"browser\";\n\treadonly providerName = \"Browser Speech Synthesis\";\n\treadonly version = \"1.0.0\";\n\n\tasync initialize(config: TTSConfig): Promise<ITTSProviderImplementation> {\n\t\t// SSR guard\n\t\tif (typeof window === \"undefined\") {\n\t\t\tthrow new Error(\"BrowserTTSProvider requires browser environment\");\n\t\t}\n\n\t\t// Check browser support\n\t\tif (!(\"speechSynthesis\" in window)) {\n\t\t\tthrow new Error(\"Browser does not support Speech Synthesis API\");\n\t\t}\n\n\t\treturn new BrowserTTSProviderImpl(config);\n\t}\n\n\tsupportsFeature(feature: TTSFeature): boolean {\n\t\t// Browser supports all TTS features\n\t\treturn true;\n\t}\n\n\tgetCapabilities(): TTSProviderCapabilities {\n\t\treturn {\n\t\t\tsupportsPause: true,\n\t\t\tsupportsResume: true,\n\t\t\tsupportsWordBoundary: true,\n\t\t\tsupportsVoiceSelection: true,\n\t\t\tsupportsRateControl: true,\n\t\t\tsupportsPitchControl: true,\n\t\t\t// The Web Speech API voices plain text only; SSML tags are read aloud.\n\t\t\tsupportsSSML: false,\n\t\t};\n\t}\n\n\tdestroy(): void {\n\t\t// No cleanup needed for browser provider\n\t}\n}\n\n/**\n * Browser TTS Provider Implementation\n *\n * Handles actual speech synthesis using the Web Speech API.\n */\nclass BrowserTTSProviderImpl implements ITTSProviderImplementation {\n\tprivate utterance: SpeechSynthesisUtterance | null = null;\n\tprivate config: TTSConfig | null = null;\n\tprivate _isPlaying = false;\n\tprivate _isPaused = false;\n\tprivate speakRunId = 0;\n\n\tconstructor(config: TTSConfig) {\n\t\tthis.config = config;\n\t}\n\n\tasync speak(text: string): Promise<void> {\n\t\tif (!this.config) {\n\t\t\tthrow new Error(\"TTS not initialized\");\n\t\t}\n\t\t// Invalidate any in-flight run and cancel current utterance.\n\t\tthis.stop();\n\t\tconst runId = this.speakRunId;\n\n\t\tconst chunks = this.splitIntoChunks(text);\n\t\tfor (const chunk of chunks) {\n\t\t\tif (runId !== this.speakRunId) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tconst shouldContinue = await this.speakChunk(\n\t\t\t\tchunk.text,\n\t\t\t\tchunk.offset,\n\t\t\t\trunId,\n\t\t\t);\n\t\t\tif (!shouldContinue) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync speakSegments(segments: TTSSpeechSegment[]): Promise<void> {\n\t\tif (!this.config) {\n\t\t\tthrow new Error(\"TTS not initialized\");\n\t\t}\n\t\tthis.stop();\n\t\tconst runId = this.speakRunId;\n\t\tfor (const segment of segments) {\n\t\t\tif (runId !== this.speakRunId) break;\n\t\t\tconst chunks = this.splitIntoChunks(segment.text);\n\t\t\tfor (const chunk of chunks) {\n\t\t\t\tif (runId !== this.speakRunId) break;\n\t\t\t\tconst shouldContinue = await this.speakChunk(\n\t\t\t\t\tchunk.text,\n\t\t\t\t\tsegment.startOffset + chunk.offset,\n\t\t\t\t\trunId,\n\t\t\t\t);\n\t\t\t\tif (!shouldContinue) break;\n\t\t\t}\n\t\t\tconst pauseMsAfter = Math.max(0, Number(segment.pauseMsAfter || 0));\n\t\t\tif (pauseMsAfter > 0 && runId === this.speakRunId) {\n\t\t\t\tconst shouldContinue = await this.waitForPause(pauseMsAfter, runId);\n\t\t\t\tif (!shouldContinue) break;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async waitForPause(pauseMs: number, runId: number): Promise<boolean> {\n\t\tawait new Promise((resolve) => setTimeout(resolve, pauseMs));\n\t\treturn runId === this.speakRunId;\n\t}\n\n\tprivate splitIntoChunks(\n\t\ttext: string,\n\t): Array<{ text: string; offset: number }> {\n\t\tconst MAX_CHUNK_LENGTH = 260;\n\t\tif (text.length <= MAX_CHUNK_LENGTH) {\n\t\t\treturn [{ text, offset: 0 }];\n\t\t}\n\n\t\tconst sentences = this.segmentSentences(text);\n\t\tconst chunks: Array<{ text: string; offset: number }> = [];\n\t\tlet currentText = \"\";\n\t\tlet currentOffset = 0;\n\n\t\tfor (const sentence of sentences) {\n\t\t\tconst trimmed = sentence.text.trim();\n\t\t\tif (!trimmed) continue;\n\t\t\tconst firstNonWhitespace = sentence.text.search(/\\S/);\n\t\t\tconst trimmedStart =\n\t\t\t\tsentence.offset + (firstNonWhitespace === -1 ? 0 : firstNonWhitespace);\n\n\t\t\tif (!currentText) {\n\t\t\t\tcurrentText = trimmed;\n\t\t\t\tcurrentOffset = trimmedStart;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst candidate = `${currentText} ${trimmed}`;\n\t\t\tif (candidate.length <= MAX_CHUNK_LENGTH) {\n\t\t\t\tcurrentText = candidate;\n\t\t\t} else {\n\t\t\t\tchunks.push({ text: currentText, offset: currentOffset });\n\t\t\t\tcurrentText = trimmed;\n\t\t\t\tcurrentOffset = trimmedStart;\n\t\t\t}\n\t\t}\n\n\t\tif (currentText) {\n\t\t\tchunks.push({ text: currentText, offset: currentOffset });\n\t\t}\n\t\treturn chunks.length ? chunks : [{ text, offset: 0 }];\n\t}\n\n\tprivate getHighlightMode(): \"word\" | \"sentence\" {\n\t\tconst providerOptions = (this.config?.providerOptions || {}) as Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>;\n\t\treturn providerOptions.highlightMode === \"word\" ? \"word\" : \"sentence\";\n\t}\n\n\tprivate getSegmentationPolicy(): {\n\t\tuseSentenceSegmenter: boolean;\n\t\tuseWordSegmenter: boolean;\n\t\tlocale?: string;\n\t} {\n\t\tconst providerOptions = (this.config?.providerOptions || {}) as Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>;\n\t\tconst segmenter = (providerOptions.segmenter || {}) as Record<\n\t\t\tstring,\n\t\t\tunknown\n\t\t>;\n\t\tconst mode = segmenter.mode;\n\t\tconst useSegmenter = mode !== \"regexOnly\";\n\t\tconst locale =\n\t\t\ttypeof segmenter.locale === \"string\" && segmenter.locale.trim().length > 0\n\t\t\t\t? segmenter.locale\n\t\t\t\t: typeof providerOptions.locale === \"string\" &&\n\t\t\t\t\t\tproviderOptions.locale.trim().length > 0\n\t\t\t\t\t? providerOptions.locale\n\t\t\t\t\t: undefined;\n\t\treturn {\n\t\t\tuseSentenceSegmenter: useSegmenter,\n\t\t\tuseWordSegmenter: useSegmenter,\n\t\t\tlocale,\n\t\t};\n\t}\n\n\tprivate segmentSentences(\n\t\ttext: string,\n\t): Array<{ text: string; offset: number }> {\n\t\tconst policy = this.getSegmentationPolicy();\n\t\treturn segmentTextToSentences(text, {\n\t\t\tlocale: policy.locale,\n\t\t\tuseSentenceSegmenter: policy.useSentenceSegmenter,\n\t\t});\n\t}\n\n\tprivate inferWordLength(text: string, index: number): number {\n\t\tconst safeIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(index, Math.max(0, text.length - 1)),\n\t\t);\n\t\tconst slice = text.slice(safeIndex);\n\t\tconst match = slice.match(/^\\s*([^\\s]+)/);\n\t\treturn match?.[1]?.length || 1;\n\t}\n\n\tprivate async speakChunk(\n\t\tchunkText: string,\n\t\tchunkOffset: number,\n\t\trunId: number,\n\t): Promise<boolean> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tif (runId !== this.speakRunId) {\n\t\t\t\tresolve(false);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst utterance = new SpeechSynthesisUtterance(chunkText);\n\t\t\tthis.utterance = utterance;\n\n\t\t\t// Apply config\n\t\t\tconst voice = findBrowserVoice(\n\t\t\t\tspeechSynthesis.getVoices(),\n\t\t\t\tthis.config?.voice,\n\t\t\t);\n\t\t\tif (voice && shouldAssignBrowserVoice(voice)) utterance.voice = voice;\n\n\t\t\tif (this.config?.rate) utterance.rate = this.config.rate;\n\t\t\tif (this.config?.pitch) utterance.pitch = this.config.pitch;\n\n\t\t\tconst clearOwnedUtterance = () => {\n\t\t\t\tif (this.utterance === utterance) {\n\t\t\t\t\tthis.utterance = null;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tutterance.onstart = () => {\n\t\t\t\tif (runId !== this.speakRunId) return;\n\t\t\t\tthis._isPlaying = true;\n\t\t\t\tthis._isPaused = false;\n\t\t\t};\n\n\t\t\tutterance.onend = () => {\n\t\t\t\tclearOwnedUtterance();\n\t\t\t\tif (runId !== this.speakRunId) {\n\t\t\t\t\tresolve(false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis._isPlaying = false;\n\t\t\t\tthis._isPaused = false;\n\t\t\t\tresolve(true);\n\t\t\t};\n\n\t\t\tutterance.onerror = (event) => {\n\t\t\t\tclearOwnedUtterance();\n\t\t\t\tif (runId !== this.speakRunId) {\n\t\t\t\t\tresolve(false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tthis._isPlaying = false;\n\t\t\t\tthis._isPaused = false;\n\t\t\t\tif (event.error === \"interrupted\" || event.error === \"canceled\") {\n\t\t\t\t\tresolve(false);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\treject(new Error(`Speech synthesis error: ${event.error}`));\n\t\t\t};\n\n\t\t\tutterance.onpause = () => {\n\t\t\t\tif (runId !== this.speakRunId) return;\n\t\t\t\tthis._isPaused = true;\n\t\t\t};\n\n\t\t\tutterance.onresume = () => {\n\t\t\t\tif (runId !== this.speakRunId) return;\n\t\t\t\tthis._isPaused = false;\n\t\t\t};\n\n\t\t\tutterance.onboundary = (event) => {\n\t\t\t\tif (runId !== this.speakRunId) return;\n\t\t\t\tconsole.log(\n\t\t\t\t\t\"[BrowserProvider] Boundary event:\",\n\t\t\t\t\tevent.name,\n\t\t\t\t\t\"charIndex:\",\n\t\t\t\t\tevent.charIndex,\n\t\t\t\t\t\"charLength:\",\n\t\t\t\t\tevent.charLength,\n\t\t\t\t);\n\t\t\t\tif (event.name !== \"word\" || !this.onWordBoundary) return;\n\t\t\t\tif (this.getHighlightMode() === \"sentence\") {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst charIndex = Math.max(\n\t\t\t\t\t0,\n\t\t\t\t\tMath.min(event.charIndex, Math.max(0, chunkText.length - 1)),\n\t\t\t\t);\n\t\t\t\tconst reportedLength = Number(event.charLength || 0);\n\t\t\t\tconst inferredLength = this.inferWordLength(chunkText, charIndex);\n\t\t\t\tconst wordLength =\n\t\t\t\t\tNumber.isFinite(reportedLength) &&\n\t\t\t\t\treportedLength > 0 &&\n\t\t\t\t\treportedLength <= 80 &&\n\t\t\t\t\tcharIndex + reportedLength <= chunkText.length\n\t\t\t\t\t\t? reportedLength\n\t\t\t\t\t\t: inferredLength;\n\t\t\t\tconst word = chunkText\n\t\t\t\t\t.substring(\n\t\t\t\t\t\tcharIndex,\n\t\t\t\t\t\tMath.min(chunkText.length, charIndex + wordLength),\n\t\t\t\t\t)\n\t\t\t\t\t.trim();\n\t\t\t\tconst absoluteBoundaryStart = chunkOffset + charIndex;\n\t\t\t\tconsole.log(\n\t\t\t\t\t\"[BrowserProvider] Calling onWordBoundary with word:\",\n\t\t\t\t\tword,\n\t\t\t\t\t\"at position:\",\n\t\t\t\t\tabsoluteBoundaryStart,\n\t\t\t\t);\n\t\t\t\tthis.onWordBoundary(word, absoluteBoundaryStart, wordLength);\n\t\t\t};\n\n\t\t\tspeechSynthesis.speak(utterance);\n\t\t});\n\t}\n\n\tpause(): void {\n\t\tif (this._isPlaying && !this._isPaused) {\n\t\t\tspeechSynthesis.pause();\n\t\t}\n\t}\n\n\tresume(): void {\n\t\tif (this._isPlaying && this._isPaused) {\n\t\t\tspeechSynthesis.resume();\n\t\t}\n\t}\n\n\tstop(): void {\n\t\tthis.speakRunId += 1;\n\t\tif (this._isPlaying || this.utterance) {\n\t\t\tspeechSynthesis.cancel();\n\t\t}\n\t\tthis.utterance = null;\n\t\tthis._isPlaying = false;\n\t\tthis._isPaused = false;\n\t}\n\n\tisPlaying(): boolean {\n\t\treturn this._isPlaying && !this._isPaused;\n\t}\n\n\tisPaused(): boolean {\n\t\treturn this._isPaused;\n\t}\n\n\t/**\n\t * Update settings dynamically (rate, pitch, voice)\n\t * Changes take effect on the next speak() call\n\t */\n\tupdateSettings(settings: Partial<TTSConfig>): void {\n\t\tif (!this.config) {\n\t\t\tthis.config = {} as TTSConfig;\n\t\t}\n\n\t\t// Update config with new settings\n\t\tif (settings.rate !== undefined) {\n\t\t\tthis.config.rate = settings.rate;\n\t\t}\n\t\tif (settings.pitch !== undefined) {\n\t\t\tthis.config.pitch = settings.pitch;\n\t\t}\n\t\tif (settings.voice !== undefined) {\n\t\t\tthis.config.voice = settings.voice;\n\t\t}\n\t\tif (settings.providerOptions !== undefined) {\n\t\t\tthis.config.providerOptions = {\n\t\t\t\t...(this.config.providerOptions || {}),\n\t\t\t\t...(settings.providerOptions || {}),\n\t\t\t};\n\t\t}\n\t}\n\n\tonWordBoundary?: (word: string, position: number, length?: number) => void;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"catalog-span-alignment.js","sourceRoot":"","sources":["../../../src/services/tts/catalog-span-alignment.ts"],"names":[],"mappings":"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","sourcesContent":["import { extractSpokenText } from \"./ssml/spoken-text.js\";\nimport {\n\tcreateSpeechAlignmentTokenPattern,\n\tnormalizeTextForSpeech,\n} from \"./text-processing.js\";\n\nexport type CatalogChunkPlaybackMode =\n\t| \"exact-word\"\n\t| \"anchor-span\"\n\t| \"region-fallback\";\n\nexport type BoundaryOffsetMode =\n\t| \"plain-spoken-text\"\n\t| \"raw-ssml\"\n\t| \"unsupported\";\n\nexport interface CatalogTextToken {\n\ttext: string;\n\tnormalized: string;\n\tstart: number;\n\tend: number;\n}\n\nexport interface CatalogSpanAnchor {\n\tspokenStart: number;\n\tspokenEnd: number;\n\tvisibleStart: number;\n\tvisibleEnd: number;\n\tscore: number;\n}\n\nexport interface CatalogSpanAlignment {\n\tspeechText: string;\n\tspokenText: string;\n\tvisibleText: string;\n\tplaybackMode: CatalogChunkPlaybackMode;\n\tboundaryOffsetMode: BoundaryOffsetMode;\n\tanchors: CatalogSpanAnchor[];\n\tconfidence: number;\n\tspokenTokens: CatalogTextToken[];\n\tvisibleTokens: CatalogTextToken[];\n\trawToSpokenOffsetMap: Map<number, number>;\n}\n\ninterface MatchCandidate {\n\tspokenStartToken: number;\n\tspokenEndToken: number;\n\tvisibleToken: number;\n\tscore: number;\n}\n\n// Shared Unicode-aware tokenizer (see `createSpeechAlignmentTokenPattern`), so\n// accented Latin and non-Latin words tokenize the same way here as in the math\n// speech tokenizer. LIMITATION (i18n): the phrase table below is still English,\n// so multi-word operator phrases (\"plus or minus\", \"divided by\") only align for\n// English speech; other locales degrade to coarse region highlighting. A fresh\n// pattern per call because of the global flag's mutable `lastIndex`.\nconst TOKEN_PATTERN = createSpeechAlignmentTokenPattern();\n\nconst NUMERIC_WORDS = new Map<string, string>([\n\t[\"zero\", \"0\"],\n\t[\"one\", \"1\"],\n\t[\"two\", \"2\"],\n\t[\"three\", \"3\"],\n\t[\"four\", \"4\"],\n\t[\"five\", \"5\"],\n\t[\"six\", \"6\"],\n\t[\"seven\", \"7\"],\n\t[\"eight\", \"8\"],\n\t[\"nine\", \"9\"],\n\t[\"ten\", \"10\"],\n]);\n\nconst SPOKEN_PHRASE_TARGETS: Array<{\n\tphrase: string[];\n\tvisible: string;\n\tscore: number;\n}> = [\n\t{ phrase: [\"plus\", \"or\", \"minus\"], visible: \"±\", score: 8 },\n\t{ phrase: [\"square\", \"root\"], visible: \"√\", score: 8 },\n\t{ phrase: [\"divided\", \"by\"], visible: \"/\", score: 7 },\n\t{ phrase: [\"all\", \"over\"], visible: \"/\", score: 7 },\n\t{ phrase: [\"equals\"], visible: \"=\", score: 7 },\n\t{ phrase: [\"equal\"], visible: \"=\", score: 7 },\n\t{ phrase: [\"times\"], visible: \"\\u2062\", score: 7 },\n\t{ phrase: [\"multiplied\", \"by\"], visible: \"\\u2062\", score: 7 },\n\t{ phrase: [\"over\"], visible: \"/\", score: 6 },\n\t{ phrase: [\"squared\"], visible: \"²\", score: 6 },\n\t{ phrase: [\"squared\"], visible: \"2\", score: 4 },\n\t{ phrase: [\"negative\"], visible: \"-\", score: 5 },\n\t{ phrase: [\"minus\"], visible: \"-\", score: 5 },\n\t{ phrase: [\"plus\"], visible: \"+\", score: 5 },\n];\n\nconst tokenize = (text: string): CatalogTextToken[] => {\n\tconst tokens: CatalogTextToken[] = [];\n\tfor (const match of text.matchAll(TOKEN_PATTERN)) {\n\t\tconst value = match[0];\n\t\tconst start = match.index ?? 0;\n\t\ttokens.push({\n\t\t\ttext: value,\n\t\t\tnormalized: value.toLowerCase(),\n\t\t\tstart,\n\t\t\tend: start + value.length,\n\t\t});\n\t}\n\treturn tokens;\n};\n\nconst normalizedTokenValue = (token: CatalogTextToken): string =>\n\tNUMERIC_WORDS.get(token.normalized) || token.normalized;\n\nconst createCandidates = (\n\tspokenTokens: CatalogTextToken[],\n\tvisibleTokens: CatalogTextToken[],\n): MatchCandidate[] => {\n\tconst candidates: MatchCandidate[] = [];\n\tfor (let spokenIndex = 0; spokenIndex < spokenTokens.length; spokenIndex++) {\n\t\tfor (\n\t\t\tlet visibleIndex = 0;\n\t\t\tvisibleIndex < visibleTokens.length;\n\t\t\tvisibleIndex++\n\t\t) {\n\t\t\tconst spokenValue = normalizedTokenValue(spokenTokens[spokenIndex]);\n\t\t\tconst visibleValue = normalizedTokenValue(visibleTokens[visibleIndex]);\n\t\t\tif (spokenValue === visibleValue) {\n\t\t\t\tcandidates.push({\n\t\t\t\t\tspokenStartToken: spokenIndex,\n\t\t\t\t\tspokenEndToken: spokenIndex + 1,\n\t\t\t\t\tvisibleToken: visibleIndex,\n\t\t\t\t\tscore: 10,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tfor (const phraseTarget of SPOKEN_PHRASE_TARGETS) {\n\t\t\tconst phraseTokens = spokenTokens.slice(\n\t\t\t\tspokenIndex,\n\t\t\t\tspokenIndex + phraseTarget.phrase.length,\n\t\t\t);\n\t\t\tif (phraseTokens.length !== phraseTarget.phrase.length) continue;\n\t\t\tif (\n\t\t\t\t!phraseTokens.every(\n\t\t\t\t\t(token, tokenIndex) =>\n\t\t\t\t\t\ttoken.normalized === phraseTarget.phrase[tokenIndex],\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tfor (\n\t\t\t\tlet visibleIndex = 0;\n\t\t\t\tvisibleIndex < visibleTokens.length;\n\t\t\t\tvisibleIndex++\n\t\t\t) {\n\t\t\t\tif (visibleTokens[visibleIndex].normalized !== phraseTarget.visible) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tcandidates.push({\n\t\t\t\t\tspokenStartToken: spokenIndex,\n\t\t\t\t\tspokenEndToken: spokenIndex + phraseTarget.phrase.length,\n\t\t\t\t\tvisibleToken: visibleIndex,\n\t\t\t\t\tscore: phraseTarget.score,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\treturn candidates;\n};\n\nconst computeAnchors = (\n\tspokenTokens: CatalogTextToken[],\n\tvisibleTokens: CatalogTextToken[],\n): CatalogSpanAnchor[] => {\n\tconst candidates = createCandidates(spokenTokens, visibleTokens).sort(\n\t\t(left, right) =>\n\t\t\tspokenTokens[left.spokenStartToken].start -\n\t\t\t\tspokenTokens[right.spokenStartToken].start ||\n\t\t\tvisibleTokens[left.visibleToken].start -\n\t\t\t\tvisibleTokens[right.visibleToken].start,\n\t);\n\tconst bestScore = new Array(candidates.length).fill(0) as number[];\n\tconst previous = new Array(candidates.length).fill(-1) as number[];\n\n\tfor (let i = 0; i < candidates.length; i++) {\n\t\tconst candidate = candidates[i];\n\t\tbestScore[i] = candidate.score;\n\t\tfor (let j = 0; j < i; j++) {\n\t\t\tconst prior = candidates[j];\n\t\t\tif (\n\t\t\t\tprior.spokenEndToken <= candidate.spokenStartToken &&\n\t\t\t\tprior.visibleToken < candidate.visibleToken\n\t\t\t) {\n\t\t\t\tconst spokenGap = candidate.spokenStartToken - prior.spokenEndToken;\n\t\t\t\tconst visibleGap = candidate.visibleToken - prior.visibleToken - 1;\n\t\t\t\tconst gapPenalty = Math.max(0, Math.abs(spokenGap - visibleGap) - 2);\n\t\t\t\tconst score = bestScore[j] + candidate.score - gapPenalty;\n\t\t\t\tif (score > bestScore[i]) {\n\t\t\t\t\tbestScore[i] = score;\n\t\t\t\t\tprevious[i] = j;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tlet bestIndex = -1;\n\tfor (let i = 0; i < bestScore.length; i++) {\n\t\tif (bestIndex < 0 || bestScore[i] > bestScore[bestIndex]) {\n\t\t\tbestIndex = i;\n\t\t}\n\t}\n\tif (bestIndex < 0) return [];\n\n\tconst chosen: MatchCandidate[] = [];\n\tlet current = bestIndex;\n\twhile (current >= 0) {\n\t\tchosen.unshift(candidates[current]);\n\t\tcurrent = previous[current];\n\t}\n\n\treturn chosen.map((candidate) => {\n\t\tconst firstSpoken = spokenTokens[candidate.spokenStartToken];\n\t\tconst lastSpoken = spokenTokens[candidate.spokenEndToken - 1];\n\t\tconst visible = visibleTokens[candidate.visibleToken];\n\t\treturn {\n\t\t\tspokenStart: firstSpoken.start,\n\t\t\tspokenEnd: lastSpoken.end,\n\t\t\tvisibleStart: visible.start,\n\t\t\tvisibleEnd: visible.end,\n\t\t\tscore: candidate.score,\n\t\t};\n\t});\n};\n\nconst scoreConfidence = (\n\tanchors: CatalogSpanAnchor[],\n\tspokenTokens: CatalogTextToken[],\n\tvisibleTokens: CatalogTextToken[],\n): number => {\n\tif (anchors.length === 0) return 0;\n\tconst tokenCoverage =\n\t\tanchors.length /\n\t\tMath.max(1, Math.min(spokenTokens.length, visibleTokens.length));\n\tconst averageScore =\n\t\tanchors.reduce((total, anchor) => total + anchor.score, 0) /\n\t\tanchors.length /\n\t\t10;\n\treturn Math.min(1, tokenCoverage * averageScore);\n};\n\nexport const createCatalogSpanAlignment = (args: {\n\tspeechText: string;\n\tvisibleText: string;\n}): CatalogSpanAlignment => {\n\tconst extracted = extractSpokenText(args.speechText);\n\tconst visibleText = normalizeTextForSpeech(args.visibleText);\n\tconst spokenText = extracted.spokenText;\n\tconst spokenTokens = tokenize(spokenText);\n\tconst visibleTokens = tokenize(visibleText);\n\tconst anchors = extracted.unsupportedSemantic\n\t\t? []\n\t\t: computeAnchors(spokenTokens, visibleTokens);\n\tconst confidence = scoreConfidence(anchors, spokenTokens, visibleTokens);\n\tconst isExact = spokenText === visibleText && spokenText.length > 0;\n\tconst hasShortSingleExactAnchor =\n\t\tanchors.length === 1 &&\n\t\tanchors[0].score === 10 &&\n\t\tspokenTokens.length <= 2 &&\n\t\tvisibleTokens.length <= 2;\n\tconst playbackMode: CatalogChunkPlaybackMode = extracted.unsupportedSemantic\n\t\t? \"region-fallback\"\n\t\t: isExact\n\t\t\t? \"exact-word\"\n\t\t\t: anchors.length >= 2 || hasShortSingleExactAnchor\n\t\t\t\t? confidence >= 0.12\n\t\t\t\t\t? \"anchor-span\"\n\t\t\t\t\t: \"region-fallback\"\n\t\t\t\t: \"region-fallback\";\n\tconst boundaryOffsetMode: BoundaryOffsetMode = extracted.unsupportedSemantic\n\t\t? \"unsupported\"\n\t\t: extracted.hasMarkup\n\t\t\t? \"raw-ssml\"\n\t\t\t: \"plain-spoken-text\";\n\n\treturn {\n\t\tspeechText: args.speechText,\n\t\tspokenText,\n\t\tvisibleText,\n\t\tplaybackMode,\n\t\tboundaryOffsetMode,\n\t\tanchors,\n\t\tconfidence,\n\t\tspokenTokens,\n\t\tvisibleTokens,\n\t\trawToSpokenOffsetMap: extracted.rawToSpokenOffsetMap,\n\t};\n};\n\nconst findTokenAtOffset = (\n\ttokens: CatalogTextToken[],\n\toffset: number,\n): CatalogTextToken | null =>\n\ttokens.find((token) => token.start <= offset && offset < token.end) || null;\n\nconst normalizeBoundaryWord = (word?: string): string | null => {\n\tif (!word) return null;\n\tif (/^<[^>]+>$/.test(word.trim())) return null;\n\tconst token = tokenize(word)[0];\n\tif (!token) return null;\n\treturn NUMERIC_WORDS.get(token.normalized) || token.normalized;\n};\n\nconst candidateMatchesBoundaryWord = (\n\talignment: CatalogSpanAlignment,\n\tcandidate: { start: number; length: number } | null,\n\tboundaryWord?: string,\n): boolean => {\n\tconst normalizedBoundary = normalizeBoundaryWord(boundaryWord);\n\tif (!candidate || !normalizedBoundary) return false;\n\tconst token = findTokenAtOffset(alignment.spokenTokens, candidate.start);\n\tif (!token) return false;\n\treturn normalizedTokenValue(token) === normalizedBoundary;\n};\n\nconst mapRawOffsetToSpokenOffset = (\n\talignment: CatalogSpanAlignment,\n\tposition: number,\n): number | null => {\n\tconst direct = alignment.rawToSpokenOffsetMap.get(position);\n\tif (direct !== undefined) return direct;\n\tfor (let offset = position; offset < alignment.speechText.length; offset++) {\n\t\tconst mapped = alignment.rawToSpokenOffsetMap.get(offset);\n\t\tif (mapped !== undefined) return mapped;\n\t\tif (alignment.speechText[offset] === \">\") break;\n\t}\n\treturn null;\n};\n\nconst resolveRawBoundaryOffset = (\n\talignment: CatalogSpanAlignment,\n\tposition: number,\n\tsafeLength: number,\n): { start: number; length: number } | null => {\n\tconst mappedStart = mapRawOffsetToSpokenOffset(alignment, position);\n\tif (mappedStart === null) return null;\n\tconst mappedEnd = mapRawOffsetToSpokenOffset(\n\t\talignment,\n\t\tMath.max(position, position + safeLength - 1),\n\t);\n\tconst token = findTokenAtOffset(alignment.spokenTokens, mappedStart);\n\tconst mappedLength =\n\t\tmappedEnd !== null && mappedEnd >= mappedStart\n\t\t\t? mappedEnd - mappedStart + 1\n\t\t\t: token\n\t\t\t\t? token.end - mappedStart\n\t\t\t\t: safeLength;\n\treturn { start: mappedStart, length: Math.max(1, mappedLength) };\n};\n\nconst resolvePlainBoundaryOffset = (\n\talignment: CatalogSpanAlignment,\n\tposition: number,\n\tsafeLength: number,\n): { start: number; length: number } | null => {\n\tif (position < 0 || position >= alignment.spokenText.length) return null;\n\tconst token = findTokenAtOffset(alignment.spokenTokens, position);\n\tif (!token) return null;\n\treturn {\n\t\tstart: position,\n\t\tlength: Math.min(safeLength, token.end - position),\n\t};\n};\n\nexport const resolveSpokenBoundaryOffset = (\n\talignment: CatalogSpanAlignment,\n\tposition: number,\n\tlength = 1,\n\tboundaryWord?: string,\n): { start: number; length: number } | null => {\n\tif (\n\t\talignment.boundaryOffsetMode === \"unsupported\" ||\n\t\t!Number.isFinite(position)\n\t) {\n\t\treturn null;\n\t}\n\tconst safeLength = Math.max(1, Number.isFinite(length) ? length : 1);\n\tif (boundaryWord && /^<[^>]+>$/.test(boundaryWord.trim())) return null;\n\tif (alignment.boundaryOffsetMode === \"raw-ssml\") {\n\t\tconst rawCandidate = resolveRawBoundaryOffset(\n\t\t\talignment,\n\t\t\tposition,\n\t\t\tsafeLength,\n\t\t);\n\t\tconst plainCandidate = resolvePlainBoundaryOffset(\n\t\t\talignment,\n\t\t\tposition,\n\t\t\tsafeLength,\n\t\t);\n\t\tif (boundaryWord) {\n\t\t\tif (candidateMatchesBoundaryWord(alignment, rawCandidate, boundaryWord)) {\n\t\t\t\treturn rawCandidate;\n\t\t\t}\n\t\t\tif (\n\t\t\t\tcandidateMatchesBoundaryWord(alignment, plainCandidate, boundaryWord)\n\t\t\t) {\n\t\t\t\treturn plainCandidate;\n\t\t\t}\n\t\t\tif (normalizeBoundaryWord(boundaryWord)) return null;\n\t\t}\n\t\treturn rawCandidate || plainCandidate;\n\t}\n\treturn (\n\t\tresolvePlainBoundaryOffset(alignment, position, safeLength) ||\n\t\tresolveRawBoundaryOffset(alignment, position, safeLength)\n\t);\n};\n\nexport const resolveVisibleSpanForBoundary = (\n\talignment: CatalogSpanAlignment,\n\tspokenOffset: number,\n): { start: number; end: number } | null => {\n\tif (alignment.playbackMode === \"region-fallback\") return null;\n\tconst exactAnchor = alignment.anchors.find(\n\t\t(anchor) =>\n\t\t\tanchor.spokenStart <= spokenOffset && spokenOffset < anchor.spokenEnd,\n\t);\n\tif (exactAnchor) {\n\t\treturn { start: exactAnchor.visibleStart, end: exactAnchor.visibleEnd };\n\t}\n\n\tlet previous: CatalogSpanAnchor | null = null;\n\tlet next: CatalogSpanAnchor | null = null;\n\tfor (const anchor of alignment.anchors) {\n\t\tif (anchor.spokenEnd <= spokenOffset) {\n\t\t\tprevious = anchor;\n\t\t\tcontinue;\n\t\t}\n\t\tif (anchor.spokenStart > spokenOffset) {\n\t\t\tnext = anchor;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (previous && next) {\n\t\treturn {\n\t\t\tstart: Math.min(previous.visibleStart, next.visibleStart),\n\t\t\tend: Math.max(previous.visibleEnd, next.visibleEnd),\n\t\t};\n\t}\n\treturn null;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"assemble-plan.js","sourceRoot":"","sources":["../../../../src/services/tts/generated-speech/assemble-plan.ts"],"names":[],"mappings":"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","sourcesContent":["import type { MathAwareSpeechChunk } from \"../math-aware-text-processing.js\";\nimport {\n\tresolveMathSpeechFromChunks,\n\ttype ResolvedMathSpeech,\n\ttype SREMathSpeechOptions,\n} from \"../math-speech.js\";\nimport { normalizeTextForSpeech } from \"../text-processing.js\";\nimport { segmentSentences as segmentTextToSentences } from \"../text-segmentation.js\";\nimport type { AssembledSegment, AssembledSpeech } from \"./types.js\";\n\ntype MathChunk = Extract<MathAwareSpeechChunk, { type: \"math\" }>;\n\n/**\n * Resolves spoken text for a single math chunk. Injectable so the runtime can\n * supply a memoized resolver (deterministic caching) and tests can stub SRE.\n * Mirrors `resolveMathSpeechFromChunks([chunk], options)`.\n */\nexport type MathSpeechResolver = (\n\tchunk: MathChunk,\n\toptions: {\n\t\tlanguage?: string;\n\t\tproduceSsml?: boolean;\n\t\tmathSpeech?: SREMathSpeechOptions;\n\t},\n) => Promise<ResolvedMathSpeech>;\n\nconst defaultMathSpeechResolver: MathSpeechResolver = (chunk, options) =>\n\tresolveMathSpeechFromChunks([chunk], options);\n\nconst normalizeLocale = (language?: string): string =>\n\t(language || \"en\").split(\"-\")[0].toLowerCase() || \"en\";\n\nconst PRESERVE_PROSE_CHUNK_TAGS = new Set([\n\t\"H1\",\n\t\"H2\",\n\t\"H3\",\n\t\"H4\",\n\t\"H5\",\n\t\"H6\",\n\t\"LI\",\n\t\"LABEL\",\n\t\"BUTTON\",\n]);\n\nconst PRESERVE_PROSE_CHUNK_ROLES = new Set([\n\t\"heading\",\n\t\"listitem\",\n\t\"option\",\n\t\"radio\",\n]);\n\nconst shouldPreserveProseChunk = (chunk: MathAwareSpeechChunk): boolean => {\n\tif (chunk.type !== \"text\" || !chunk.sourceElement) return false;\n\tconst tagName = chunk.sourceElement.tagName.toUpperCase();\n\tconst role = (chunk.sourceElement.getAttribute(\"role\") || \"\").toLowerCase();\n\treturn (\n\t\tPRESERVE_PROSE_CHUNK_TAGS.has(tagName) ||\n\t\tPRESERVE_PROSE_CHUNK_ROLES.has(role)\n\t);\n};\n\n/**\n * Assemble an anchor-free speech plan from already-collected math-aware chunks.\n *\n * PURE: no DOM walking, no app-service imports. Preserves the spoken-text and\n * visible-span bookkeeping from the inline generated path, so the runtime output\n * is stable once serialized to plain text.\n */\nexport const assembleGeneratedSpeech = async (args: {\n\tchunks: ReadonlyArray<MathAwareSpeechChunk>;\n\tvisibleText: string;\n\tlanguage?: string;\n\tmathSpeech?: SREMathSpeechOptions;\n\t/** Request SRE SSML per math equation (used by the SSML playback format). */\n\tproduceSsml?: boolean;\n\tresolveMathSpeech?: MathSpeechResolver;\n}): Promise<AssembledSpeech> => {\n\tconst resolveMathSpeech = args.resolveMathSpeech ?? defaultMathSpeechResolver;\n\tconst { visibleText } = args;\n\tconst segments: AssembledSegment[] = [];\n\n\t// Cursor-based span tracking:\n\t// locate each chunk's normalized text within the aggregate visible text,\n\t// advancing a cursor so math fallbacks reserve their span even though only\n\t// prose spans are sliced for highlighting.\n\tlet visibleCursor = 0;\n\tconst nextVisibleSpan = (text: string): { start: number; end: number } => {\n\t\tconst normalized = normalizeTextForSpeech(text);\n\t\tconst found = visibleText.indexOf(normalized, visibleCursor);\n\t\tconst start = found >= 0 ? found : visibleCursor;\n\t\tconst end = start + normalized.length;\n\t\tvisibleCursor = end;\n\t\treturn { start, end };\n\t};\n\n\tconst speechParts: string[] = [];\n\n\tfor (let index = 0; index < args.chunks.length; index++) {\n\t\tconst chunk = args.chunks[index];\n\t\tif (chunk.type === \"text\") {\n\t\t\tconst proseSegments = shouldPreserveProseChunk(chunk)\n\t\t\t\t? [{ text: chunk.text, offset: 0 }]\n\t\t\t\t: segmentTextToSentences(chunk.text, {\n\t\t\t\t\t\tlocale: args.language,\n\t\t\t\t\t});\n\t\t\tfor (const prose of proseSegments) {\n\t\t\t\tconst text = normalizeTextForSpeech(prose.text);\n\t\t\t\tif (!text) continue;\n\t\t\t\tconst visibleSpan = nextVisibleSpan(text);\n\t\t\t\tsegments.push({\n\t\t\t\t\tsourceChunkIndex: index,\n\t\t\t\t\tsegment: {\n\t\t\t\t\t\tkind: \"prose\",\n\t\t\t\t\t\tspokenText: text,\n\t\t\t\t\t\tvisibleText: text,\n\t\t\t\t\t\tvisibleSpan,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tspeechParts.push(text);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst visibleSpan = nextVisibleSpan(chunk.fallbackText);\n\t\tconst generated = await resolveMathSpeech(chunk, {\n\t\t\tlanguage: args.language,\n\t\t\tproduceSsml: args.produceSsml,\n\t\t\tmathSpeech: args.mathSpeech,\n\t\t});\n\t\tconst usedFallback = !generated.speechText;\n\t\tconst spokenText = generated.speechText || chunk.fallbackText;\n\t\tsegments.push({\n\t\t\tsourceChunkIndex: index,\n\t\t\tsegment: {\n\t\t\t\tkind: \"math\",\n\t\t\t\tspokenText,\n\t\t\t\tmathml: chunk.mathml,\n\t\t\t\tfallbackText: chunk.fallbackText,\n\t\t\t\tvisibleSpan,\n\t\t\t\tusedFallback,\n\t\t\t\tssml: generated.ssml,\n\t\t\t},\n\t\t});\n\t\tspeechParts.push(spokenText);\n\t}\n\n\treturn {\n\t\tsegments,\n\t\tlocale: normalizeLocale(args.language),\n\t\tvisibleText,\n\t\tplainSpeechText: normalizeTextForSpeech(speechParts.join(\" \")),\n\t};\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"build-dom-plan.js","sourceRoot":"","sources":["../../../../../src/services/tts/generated-speech/dom/build-dom-plan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAKjF,OAAO,EACN,uBAAuB,GAEvB,MAAM,qBAAqB,CAAC;AAK7B,MAAM,eAAe,GAAG,CACvB,GAAsB,EACtB,KAAa,EACb,GAAW,EACS,EAAE;IACtB,MAAM,MAAM,GAAsB,IAAI,GAAG,EAAE,CAAC;IAC5C,KAAK,IAAI,KAAK,GAAG,KAAK,EAAE,KAAK,GAAG,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC;QAC9C,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC/B,IAAI,OAAO,EAAE,CAAC;YACb,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,KAAK,EAAE,OAAO,CAAC,CAAC;QACpC,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AASF;;;;GAIG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,KAAK,EAAE,IAQlD,EAAuC,EAAE;IACzC,MAAM,SAAS,GAAG,0BAA0B,CAC3C,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,qBAAqB,CAC1B,CAAC;IACF,MAAM,SAAS,GAAG,MAAM,uBAAuB,CAAC;QAC/C,MAAM,EAAE,SAAS,CAAC,MAAM;QACxB,WAAW,EAAE,SAAS,CAAC,WAAW;QAClC,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;KACzC,CAAC,CAAC;IAEH,MAAM,KAAK,GAA0B,SAAS,CAAC,QAAQ,CAAC,GAAG,CAC1D,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,EAAE,EAAE;QACjC,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;QACvD,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC9B,MAAM,aAAa,GAClB,WAAW,EAAE,IAAI,KAAK,MAAM;gBAC3B,CAAC,CAAC,CAAC,WAAW,CAAC,aAAa,IAAI,IAAI,CAAC,WAAW,CAAC;gBACjD,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC;YACrB,OAAO;gBACN,OAAO;gBACP,MAAM,EAAE;oBACP,aAAa;oBACb,aAAa,EAAE,qBAAqB,CACnC,aAAa,EACb,IAAI,CAAC,WAAW,CAChB;oBACD,UAAU,EAAE,eAAe,CAC1B,SAAS,CAAC,GAAG,EACb,OAAO,CAAC,WAAW,CAAC,KAAK,EACzB,OAAO,CAAC,WAAW,CAAC,GAAG,CACvB;iBACD;aACD,CAAC;QACH,CAAC;QACD,MAAM,WAAW,GAChB,WAAW,EAAE,IAAI,KAAK,MAAM;YAC3B,CAAC,CAAC,CAAC,WAAW,CAAC,aAAa,IAAI,IAAI,CAAC;YACrC,CAAC,CAAC,IAAI,CAAC;QACT,OAAO;YACN,OAAO;YACP,MAAM,EAAE;gBACP,aAAa,EAAE,WAAW;gBAC1B,aAAa,EAAE,WAAW;aAC1B;SACD,CAAC;IACH,CAAC,CACD,CAAC;IAEF,OAAO;QACN,IAAI,EAAE;YACL,KAAK;YACL,MAAM,EAAE,SAAS,CAAC,MAAM;YACxB,WAAW,EAAE,SAAS,CAAC,WAAW;YAClC,eAAe,EAAE,SAAS,CAAC,eAAe;SAC1C;QACD,kBAAkB,EAAE,SAAS,CAAC,kBAAkB;QAChD,WAAW,EAAE,SAAS,CAAC,WAAW;KAClC,CAAC;AACH,CAAC,CAAC","sourcesContent":["import { resolveReadableRegion } from \"../../highlight-pipeline/index.js\";\nimport { collectMathAwareTextAndMap } from \"../../math-aware-text-processing.js\";\nimport type {\n\tNormalizedTextMap,\n\tTextProcessingOptions,\n} from \"../../text-processing.js\";\nimport {\n\tassembleGeneratedSpeech,\n\ttype MathSpeechResolver,\n} from \"../assemble-plan.js\";\nimport type { SREMathSpeechOptions } from \"../../math-speech.js\";\nimport type { GeneratedSpeechPlan, PlanItem } from \"../types.js\";\nimport type { DomAnchor } from \"./types.js\";\n\nconst sliceVisibleMap = (\n\tmap: NormalizedTextMap,\n\tstart: number,\n\tend: number,\n): NormalizedTextMap => {\n\tconst sliced: NormalizedTextMap = new Map();\n\tfor (let index = start; index < end; index++) {\n\t\tconst mapping = map.get(index);\n\t\tif (mapping) {\n\t\t\tsliced.set(index - start, mapping);\n\t\t}\n\t}\n\treturn sliced;\n};\n\nexport interface BuildGeneratedSpeechResult {\n\tplan: GeneratedSpeechPlan<DomAnchor>;\n\tcontainsMathMarkup: boolean;\n\t/** Aggregate visible text from the DOM walk (may be empty). */\n\tvisibleText: string;\n}\n\n/**\n * DOM adapter: walk a live content root, assemble the (pure) speech plan, and\n * bind each segment to its live-DOM anchor. This is the only generated-speech\n * entry point that touches the DOM; assembly and serialization stay pure.\n */\nexport const buildGeneratedSpeechFromRoot = async (args: {\n\tcontentRoot: Element;\n\tlanguage?: string;\n\tmathSpeech?: SREMathSpeechOptions;\n\ttextProcessingOptions?: TextProcessingOptions;\n\t/** Request SRE SSML per equation (for the SSML playback format). */\n\tproduceSsml?: boolean;\n\tresolveMathSpeech?: MathSpeechResolver;\n}): Promise<BuildGeneratedSpeechResult> => {\n\tconst extracted = collectMathAwareTextAndMap(\n\t\targs.contentRoot,\n\t\targs.textProcessingOptions,\n\t);\n\tconst assembled = await assembleGeneratedSpeech({\n\t\tchunks: extracted.chunks,\n\t\tvisibleText: extracted.visibleText,\n\t\tlanguage: args.language,\n\t\tmathSpeech: args.mathSpeech,\n\t\tproduceSsml: args.produceSsml,\n\t\tresolveMathSpeech: args.resolveMathSpeech,\n\t});\n\n\tconst items: PlanItem<DomAnchor>[] = assembled.segments.map(\n\t\t({ segment, sourceChunkIndex }) => {\n\t\t\tconst sourceChunk = extracted.chunks[sourceChunkIndex];\n\t\t\tif (segment.kind === \"prose\") {\n\t\t\t\tconst sourceElement =\n\t\t\t\t\tsourceChunk?.type === \"text\"\n\t\t\t\t\t\t? (sourceChunk.sourceElement ?? args.contentRoot)\n\t\t\t\t\t\t: args.contentRoot;\n\t\t\t\treturn {\n\t\t\t\t\tsegment,\n\t\t\t\t\tanchor: {\n\t\t\t\t\t\tsourceElement,\n\t\t\t\t\t\tregionElement: resolveReadableRegion(\n\t\t\t\t\t\t\tsourceElement,\n\t\t\t\t\t\t\targs.contentRoot,\n\t\t\t\t\t\t),\n\t\t\t\t\t\tvisibleMap: sliceVisibleMap(\n\t\t\t\t\t\t\textracted.map,\n\t\t\t\t\t\t\tsegment.visibleSpan.start,\n\t\t\t\t\t\t\tsegment.visibleSpan.end,\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst mathElement =\n\t\t\t\tsourceChunk?.type === \"math\"\n\t\t\t\t\t? (sourceChunk.sourceElement ?? null)\n\t\t\t\t\t: null;\n\t\t\treturn {\n\t\t\t\tsegment,\n\t\t\t\tanchor: {\n\t\t\t\t\tsourceElement: mathElement,\n\t\t\t\t\tregionElement: mathElement,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t);\n\n\treturn {\n\t\tplan: {\n\t\t\titems,\n\t\t\tlocale: assembled.locale,\n\t\t\tvisibleText: assembled.visibleText,\n\t\t\tplainSpeechText: assembled.plainSpeechText,\n\t\t},\n\t\tcontainsMathMarkup: extracted.containsMathMarkup,\n\t\tvisibleText: extracted.visibleText,\n\t};\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"to-chunk-inputs.js","sourceRoot":"","sources":["../../../../../src/services/tts/generated-speech/dom/to-chunk-inputs.ts"],"names":[],"mappings":"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","sourcesContent":["import { createCatalogSpanAlignment } from \"../../catalog-span-alignment.js\";\nimport { createRangeFromVisibleMap } from \"../../highlight-pipeline/visible-map-range.js\";\nimport { createMathAwareAlignment } from \"../../math-alignment/index.js\";\nimport type {\n\tGeneratedSpeechPlan,\n\tMathSegment,\n\tProseSegment,\n} from \"../types.js\";\nimport type { DomAnchor, GeneratedSpeechChunk } from \"./types.js\";\n\nexport interface PlanToChunksOptions {\n\t/**\n\t * Playback format:\n\t * - \"plain\" (default): plain spoken text for every chunk (Phase A, the\n\t * stable generated-speech behavior).\n\t * - \"ssml\": math chunks with SRE SSML send that SSML to the provider (with\n\t * a plain fallback attached); prose chunks always stay plain.\n\t */\n\tformat?: \"plain\" | \"ssml\";\n}\n\nconst buildProseChunk = (\n\tsegment: ProseSegment,\n\tanchor: DomAnchor,\n): GeneratedSpeechChunk => {\n\tconst alignment = createCatalogSpanAlignment({\n\t\tspeechText: segment.spokenText,\n\t\tvisibleText: segment.spokenText,\n\t});\n\treturn {\n\t\tspeechText: segment.spokenText,\n\t\tvisibleText: segment.visibleText,\n\t\tsourceElement: anchor.sourceElement,\n\t\tregionElement: anchor.regionElement,\n\t\tregionRange:\n\t\t\tcreateRangeFromVisibleMap(\n\t\t\t\tanchor.visibleMap,\n\t\t\t\t0,\n\t\t\t\tsegment.visibleText.length,\n\t\t\t) ?? undefined,\n\t\tspeechMatchesVisibleText: true,\n\t\tplaybackMode: alignment.playbackMode,\n\t\talignment,\n\t\tvisibleMap: anchor.visibleMap,\n\t};\n};\n\nconst buildPlainMathChunk = (\n\tsegment: MathSegment,\n\tanchor: DomAnchor,\n): GeneratedSpeechChunk => {\n\tconst mathElement = anchor.sourceElement;\n\treturn {\n\t\tspeechText: segment.spokenText,\n\t\tvisibleText: segment.fallbackText,\n\t\tsourceElement: mathElement,\n\t\tregionElement: anchor.regionElement,\n\t\tspeechMatchesVisibleText: segment.spokenText === segment.fallbackText,\n\t\tmathAlignment: mathElement\n\t\t\t? createMathAwareAlignment({\n\t\t\t\t\tmathElement,\n\t\t\t\t\tspeechText: segment.spokenText,\n\t\t\t\t})\n\t\t\t: undefined,\n\t};\n};\n\nconst buildSsmlMathChunk = (\n\tsegment: MathSegment,\n\tanchor: DomAnchor,\n\tssml: string,\n): GeneratedSpeechChunk => {\n\tconst mathElement = anchor.sourceElement;\n\treturn {\n\t\tspeechText: ssml,\n\t\tvisibleText: segment.fallbackText,\n\t\tsourceElement: mathElement,\n\t\tregionElement: anchor.regionElement,\n\t\t// SSML never equals the visible fallback; the region-vs-token decision is\n\t\t// driven by alignment confidence, not by this flag.\n\t\tspeechMatchesVisibleText: false,\n\t\t// SINGLE SOURCE OF TRUTH: the exact SSML string sent to the provider is\n\t\t// also the string alignment tokenizes, so provider word-boundary offsets\n\t\t// (into the raw SSML) map back to spoken tokens via `rawToSpokenOffsetMap`.\n\t\tmathAlignment: mathElement\n\t\t\t? createMathAwareAlignment({ mathElement, speechText: ssml })\n\t\t\t: undefined,\n\t\t// Defensive net: if the provider rejects the SSML, retry with plain text.\n\t\tplainFallback: buildPlainMathChunk(segment, anchor),\n\t};\n};\n\n/**\n * Convert an anchored speech plan into playback chunks for\n * `TTSService.speakCatalogChunk`. The aggregate `contentToSpeak` stays plain\n * (see `plan.plainSpeechText`), so no SSML leaks into break-detection or seek\n * planning — SSML lives only in per-chunk `speechText`.\n */\nexport const planToCompositionChunkInputs = (\n\tplan: GeneratedSpeechPlan<DomAnchor>,\n\toptions: PlanToChunksOptions = {},\n): GeneratedSpeechChunk[] =>\n\tplan.items.map(({ segment, anchor }) => {\n\t\tif (segment.kind === \"prose\") return buildProseChunk(segment, anchor);\n\t\tif (options.format === \"ssml\" && segment.ssml) {\n\t\t\treturn buildSsmlMathChunk(segment, anchor, segment.ssml);\n\t\t}\n\t\treturn buildPlainMathChunk(segment, anchor);\n\t});\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../../src/services/tts/generated-speech/dom/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n\tCatalogChunkPlaybackMode,\n\tCatalogSpanAlignment,\n} from \"../../catalog-span-alignment.js\";\nimport type { SpeechCompositionChunkInput } from \"../../highlight-pipeline/index.js\";\nimport type { MathAwareAlignment } from \"../../math-alignment/index.js\";\nimport type { NormalizedTextMap } from \"../../text-processing.js\";\n\n/**\n * Live-DOM anchor for a plan segment (runtime binding). The DOM adapter is the\n * only place that produces these; the pure core never references DOM nodes.\n */\nexport interface DomAnchor {\n\tsourceElement: Element | null;\n\tregionElement: Element | null;\n\t/**\n\t * Prose only: per-segment slice of the visible-text map, used to resolve\n\t * highlight ranges. Math segments highlight via `mathAlignment` instead.\n\t */\n\tvisibleMap?: NormalizedTextMap;\n}\n\n/**\n * Playback chunk consumed by `TTSService.speakCatalogChunk`. Structurally the\n * same shape the authored-catalog path produces; extends the highlight\n * pipeline's stable {@link SpeechCompositionChunkInput} with the two playback\n * fields `speakCatalogChunk` reads directly (`playbackMode`,\n * `speechMatchesVisibleText`).\n */\nexport interface GeneratedSpeechChunk extends SpeechCompositionChunkInput {\n\tspeechMatchesVisibleText: boolean;\n\tplaybackMode?: CatalogChunkPlaybackMode;\n\t/**\n\t * Plain-text variant of this chunk for speak-time fallback. Present only on\n\t * SSML chunks; if the provider rejects the SSML `speechText`, the runtime\n\t * retries with this plain chunk (same anchors, plain alignment). Browsers\n\t * and SSML-incapable providers never receive the SSML variant in the first\n\t * place (build-time provider gating), so this is a defensive net.\n\t */\n\tplainFallback?: GeneratedSpeechChunk;\n}\n\nexport type { CatalogSpanAlignment, MathAwareAlignment };\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/services/tts/generated-speech/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,cAAc,YAAY,CAAC;AAC3B,OAAO,EACN,uBAAuB,GAEvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACN,gCAAgC,GAEhC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,4BAA4B,GAE5B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["/**\n * Generated-speech module (PIE-623): runtime speech composition for math items\n * that ship without authored SSML / `accessibilityCatalogs`.\n *\n * Layering:\n * - Pure core (`./assemble-plan`, `./math-speech-cache`, `./types`): no DOM\n * walking, no runtime/Svelte imports. Enforced by\n * `scripts/check-speech-composition-purity.mjs`.\n * - DOM adapter (`./dom/*`): binds the plan to live DOM and emits playback\n * chunks for `TTSService`.\n */\n\nexport * from \"./types.js\";\nexport {\n\tassembleGeneratedSpeech,\n\ttype MathSpeechResolver,\n} from \"./assemble-plan.js\";\nexport {\n\tcreateMemoizedMathSpeechResolver,\n\ttype MathSpeechCacheOptions,\n} from \"./math-speech-cache.js\";\nexport {\n\tbuildGeneratedSpeechFromRoot,\n\ttype BuildGeneratedSpeechResult,\n} from \"./dom/build-dom-plan.js\";\nexport { planToCompositionChunkInputs } from \"./dom/to-chunk-inputs.js\";\nexport type { DomAnchor, GeneratedSpeechChunk } from \"./dom/types.js\";\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"math-speech-cache.js","sourceRoot":"","sources":["../../../../src/services/tts/generated-speech/math-speech-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,6BAA6B,EAC7B,2BAA2B,GAG3B,MAAM,mBAAmB,CAAC;AAG3B,MAAM,eAAe,GAAG,CAAC,QAAiB,EAAU,EAAE,CACrD,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,IAAI,CAAC;AAExD,0EAA0E;AAC1E,+EAA+E;AAC/E,MAAM,iBAAiB,GAAG,QAAQ,CAAC;AAEnC,MAAM,mBAAmB,GAAG,GAAG,CAAC;AAEhC,MAAM,eAAe,GAAG,CAAC,KAAc,EAAU,EAAE;IAClD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,UAAU,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAC1D,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,WAAW,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;aAChE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;aACpD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;aACzE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAChB,CAAC;IACD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,WAAW,CAAC;IACvC,IAAI,OAAO,KAAK,KAAK,WAAW;QAAE,OAAO,YAAY,CAAC;IACtD,IAAI,OAAO,KAAK,KAAK,UAAU;QAC9B,OAAO,YAAY,KAAK,CAAC,IAAI,IAAI,WAAW,EAAE,CAAC;IAChD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,UAAU,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAChE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,UAAU,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC;IACnE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC/B,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;YAC5B,CAAC,CAAC,UAAU,KAAK,EAAE;YACnB,CAAC,CAAC,UAAU,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;IAC9B,CAAC;IACD,OAAO,GAAG,OAAO,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;AACnD,CAAC,CAAC;AAEF,MAAM,WAAW,GAAG,CACnB,QAA4B,EAC5B,WAAoB,EACpB,UAA4C,EAC5C,MAAc,EACL,EAAE,CACX,GAAG,iBAAiB,SAAS,eAAe,CAAC,QAAQ,CAAC,SACrD,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OACxB,SAAS,eAAe,CAAC,UAAU,IAAI,IAAI,CAAC,SAAS,MAAM,EAAE,CAAC;AAQ/D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAC/C,UAAkC,EAAE,EACf,EAAE;IACvB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,UAAU,IAAI,mBAAmB,CAAC,CAAC;IAC1E,MAAM,OAAO,GACZ,OAAO,CAAC,OAAO;QACf,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,2BAA2B,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IAC/D,MAAM,KAAK,GAAG,IAAI,GAAG,EAA8B,CAAC;IACpD,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,KAAyB,EAAQ,EAAE;QAClE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACtB,OAAO,KAAK,CAAC,IAAI,GAAG,UAAU,EAAE,CAAC;YAChC,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC;YACzC,IAAI,MAAM,KAAK,SAAS;gBAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAChD,CAAC;IACF,CAAC,CAAC;IAEF,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAC5B,MAAM,UAAU,GAAG,6BAA6B,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAClE,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,KAAK,IAAI,CAAC;QAC9C,MAAM,GAAG,GAAG,WAAW,CACtB,IAAI,CAAC,QAAQ,EACb,WAAW,EACX,UAAU,EACV,KAAK,CAAC,MAAM,CACZ,CAAC;QACF,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,MAAM,EAAE,CAAC;YACZ,uBAAuB;YACvB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAClB,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACvB,OAAO,MAAM,CAAC;QACf,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC;QAC/D,IAAI,WAAW,IAAI,QAAQ,CAAC,IAAI,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACpD,SAAS,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE;gBACtE,UAAU,EAAE,QAAQ,CAAC,UAAU;gBAC/B,cAAc,EAAE,QAAQ,CAAC,cAAc;gBACvC,YAAY,EAAE,QAAQ,CAAC,YAAY;aACnC,CAAC,CAAC;QACJ,CAAC;QACD,SAAS,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACzB,OAAO,QAAQ,CAAC;IACjB,CAAC,CAAC;AACH,CAAC,CAAC","sourcesContent":["import {\n\tnormalizeSREMathSpeechOptions,\n\tresolveMathSpeechFromChunks,\n\ttype ResolvedMathSpeech,\n\ttype SREMathSpeechOptions,\n} from \"../math-speech.js\";\nimport type { MathSpeechResolver } from \"./assemble-plan.js\";\n\nconst normalizeLocale = (language?: string): string =>\n\t(language || \"en\").split(\"-\")[0].toLowerCase() || \"en\";\n\n// Bump when speech-rule-engine output semantics change in a way that must\n// invalidate cached spoken text (e.g. an SRE upgrade with different phrasing).\nconst SRE_CACHE_VERSION = \"sre-v1\";\n\nconst DEFAULT_MAX_ENTRIES = 256;\n\nconst stableStringify = (value: unknown): string => {\n\tif (Array.isArray(value)) {\n\t\treturn `array:[${value.map(stableStringify).join(\",\")}]`;\n\t}\n\tif (value && typeof value === \"object\") {\n\t\treturn `object:{${Object.entries(value as Record<string, unknown>)\n\t\t\t.sort(([left], [right]) => left.localeCompare(right))\n\t\t\t.map(([key, entry]) => `${JSON.stringify(key)}:${stableStringify(entry)}`)\n\t\t\t.join(\",\")}}`;\n\t}\n\tif (value === null) return \"null:null\";\n\tif (typeof value === \"undefined\") return \"undefined:\";\n\tif (typeof value === \"function\")\n\t\treturn `function:${value.name || \"anonymous\"}`;\n\tif (typeof value === \"symbol\") return `symbol:${String(value)}`;\n\tif (typeof value === \"bigint\") return `bigint:${value.toString()}`;\n\tif (typeof value === \"number\") {\n\t\treturn Number.isFinite(value)\n\t\t\t? `number:${value}`\n\t\t\t: `number:${String(value)}`;\n\t}\n\treturn `${typeof value}:${JSON.stringify(value)}`;\n};\n\nconst cacheKeyFor = (\n\tlanguage: string | undefined,\n\tproduceSsml: boolean,\n\tmathSpeech: SREMathSpeechOptions | undefined,\n\tmathml: string,\n): string =>\n\t`${SRE_CACHE_VERSION}\\u0000${normalizeLocale(language)}\\u0000${\n\t\tproduceSsml ? \"ssml\" : \"plain\"\n\t}\\u0000${stableStringify(mathSpeech ?? null)}\\u0000${mathml}`;\n\nexport interface MathSpeechCacheOptions {\n\tmaxEntries?: number;\n\t/** Underlying resolver; defaults to the real SRE call. Injectable for tests. */\n\tresolve?: MathSpeechResolver;\n}\n\n/**\n * Build a memoized {@link MathSpeechResolver}.\n *\n * Caches only DOM-free spoken-text results keyed by\n * `(SRE version, locale, output mode, SRE mathSpeech options, MathML source)`.\n * Never caches DOM nodes or maps. Bounded by a simple LRU so long sessions do\n * not grow unbounded.\n */\nexport const createMemoizedMathSpeechResolver = (\n\toptions: MathSpeechCacheOptions = {},\n): MathSpeechResolver => {\n\tconst maxEntries = Math.max(1, options.maxEntries ?? DEFAULT_MAX_ENTRIES);\n\tconst resolve =\n\t\toptions.resolve ??\n\t\t((chunk, opts) => resolveMathSpeechFromChunks([chunk], opts));\n\tconst cache = new Map<string, ResolvedMathSpeech>();\n\tconst setCached = (key: string, value: ResolvedMathSpeech): void => {\n\t\tcache.delete(key);\n\t\tcache.set(key, value);\n\t\twhile (cache.size > maxEntries) {\n\t\t\tconst oldest = cache.keys().next().value;\n\t\t\tif (oldest !== undefined) cache.delete(oldest);\n\t\t}\n\t};\n\n\treturn async (chunk, opts) => {\n\t\tconst mathSpeech = normalizeSREMathSpeechOptions(opts.mathSpeech);\n\t\tconst produceSsml = opts.produceSsml === true;\n\t\tconst key = cacheKeyFor(\n\t\t\topts.language,\n\t\t\tproduceSsml,\n\t\t\tmathSpeech,\n\t\t\tchunk.mathml,\n\t\t);\n\t\tconst cached = cache.get(key);\n\t\tif (cached) {\n\t\t\t// Refresh LRU recency.\n\t\t\tcache.delete(key);\n\t\t\tcache.set(key, cached);\n\t\t\treturn cached;\n\t\t}\n\t\tconst resolved = await resolve(chunk, { ...opts, mathSpeech });\n\t\tif (produceSsml && resolved.ssml && maxEntries > 1) {\n\t\t\tsetCached(cacheKeyFor(opts.language, false, mathSpeech, chunk.mathml), {\n\t\t\t\tspeechText: resolved.speechText,\n\t\t\t\tusedMathSpeech: resolved.usedMathSpeech,\n\t\t\t\tusedFallback: resolved.usedFallback,\n\t\t\t});\n\t\t}\n\t\tsetCached(key, resolved);\n\t\treturn resolved;\n\t};\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/services/tts/generated-speech/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG","sourcesContent":["/**\n * Generated-speech plan model (PURE core).\n *\n * This module is the speech-composition core for items that contain math but\n * ship without authored SSML / `accessibilityCatalogs` (PIE-623). It is\n * deliberately free of any imports from `TTSService`, the highlight renderer,\n * `HighlightCoordinator`, Svelte, or custom-element entrypoints\n * (`scripts/check-speech-composition-purity.mjs` enforces this). DOM primitive\n * types (`Element`) and an injectable SRE resolver are allowed; live-DOM\n * walking, anchor binding, alignment, and rendering live in `./dom/`.\n *\n * The plan is generic over `Anchor` so the same assembly + SSML serialization\n * can be reused by a future authoring/persistable path (serializable locators)\n * without rewriting the core. The runtime binds live DOM via `DomAnchor`\n * (see `./dom/types.ts`).\n */\n\nexport type StructuralBreakStrength = \"weak\" | \"medium\" | \"strong\";\n\nexport interface ProseSegment {\n\tkind: \"prose\";\n\t/** Spoken words for this prose run. Identical to `visibleText` today. */\n\tspokenText: string;\n\t/** Visible text run this segment was collected from. */\n\tvisibleText: string;\n\t/**\n\t * Character span of this run within the plan's aggregate `visibleText`.\n\t * Used by the DOM adapter to slice the visible-text map for highlighting.\n\t */\n\tvisibleSpan: { start: number; end: number };\n}\n\nexport interface MathSegment {\n\tkind: \"math\";\n\t/** Spoken words for the equation (SRE output, or the visible fallback). */\n\tspokenText: string;\n\t/** Canonical MathML for the equation. */\n\tmathml: string;\n\t/** Visible fallback text (rendered glyphs) for the equation. */\n\tfallbackText: string;\n\t/** Character span of `fallbackText` within the plan's aggregate visible text. */\n\tvisibleSpan: { start: number; end: number };\n\t/** True when SRE produced nothing and the visible fallback is spoken instead. */\n\tusedFallback: boolean;\n\t/**\n\t * SRE SSML rendering of the equation (a self-contained `<speak>` document),\n\t * present only when SSML production was requested and SRE produced trackable\n\t * SSML. Highlight-safe (structural tags only); the same string is fed to the\n\t * provider and to alignment so raw-SSML word offsets map correctly.\n\t */\n\tssml?: string;\n}\n\n/**\n * A structural pause. Breaks are never standalone speakable units: the DOM\n * adapter folds them into the SSML of an adjacent speakable segment (or relies\n * on the gap between sequential per-chunk utterances). Modeled here so the\n * plan can carry pause intent for the SSML serializer.\n */\nexport interface StructuralBreak {\n\tkind: \"break\";\n\tstrength: StructuralBreakStrength;\n}\n\nexport type SpeechSegment = ProseSegment | MathSegment;\nexport type PlanSegment = SpeechSegment | StructuralBreak;\n\n/** One assembled (still anchor-free) segment plus its source chunk index. */\nexport interface AssembledSegment {\n\tsegment: SpeechSegment;\n\t/** Index into the source `MathAwareSpeechChunk[]` used to attach anchors. */\n\tsourceChunkIndex: number;\n}\n\n/** Pure assembly output: spoken text + visible spans, no DOM anchors yet. */\nexport interface AssembledSpeech {\n\tsegments: AssembledSegment[];\n\tlocale: string;\n\t/** Aggregate visible text (prose + math fallbacks) in document order. */\n\tvisibleText: string;\n\t/**\n\t * Aggregate plain spoken text (NEVER SSML). Carried so playback metadata /\n\t * flags can stay plain even when per-chunk payloads become SSML.\n\t */\n\tplainSpeechText: string;\n}\n\n/**\n * One ordered plan entry pairing a content segment with an opaque `Anchor`\n * that points back at its source.\n */\nexport interface PlanItem<Anchor> {\n\tsegment: SpeechSegment;\n\tanchor: Anchor;\n}\n\n/**\n * An anchored speech plan, generic over the anchor type. `serializeToSsml`\n * consumes it without touching `Anchor` (content is anchor-free); the DOM\n * adapter consumes it to build highlight chunks.\n */\nexport interface GeneratedSpeechPlan<Anchor> {\n\titems: PlanItem<Anchor>[];\n\tlocale: string;\n\tvisibleText: string;\n\tplainSpeechText: string;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"boundary-normalizer.js","sourceRoot":"","sources":["../../../../src/services/tts/highlight-pipeline/boundary-normalizer.ts"],"names":[],"mappings":"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","sourcesContent":["import { resolveSpokenBoundaryOffset } from \"../catalog-span-alignment.js\";\nimport {\n\tresolveBoundaryToSpeechToken,\n\ttokenizeSpeechSource,\n} from \"../math-alignment/speech-tokenizer.js\";\nimport type {\n\tNormalizedBoundaryEvent,\n\tTTSBoundaryEvent,\n\tTTSHighlightChunk,\n} from \"./types.js\";\n\nconst normalizeBoundaryWord = (word: string): string | null => {\n\tif (!word || /^<[^>]+>$/.test(word.trim())) return null;\n\treturn (\n\t\ttokenizeSpeechSource({ speechText: word }).tokens[0]?.normalized || null\n\t);\n};\n\n// Resolve a provider boundary for a chunk that carries no catalog span\n// alignment — notably the generated-SSML math path, whose `speechText` is a\n// raw `<speak>` document. The provider's offset is an index into `speechText`\n// exactly as it was sent (which may include SSML tags), so we tokenize that\n// same string and let the shared speech tokenizer translate the offset into the\n// extracted spoken text. Returns the boundary in spoken-text space alongside\n// that spoken text, so the downstream word/offset checks share one coordinate\n// system. The previous \"direct\" path treated the raw-SSML offset as a\n// spoken-text offset and rejected every math boundary (PIE-623 regression).\nconst speechSourceBoundary = (\n\tchunk: TTSHighlightChunk,\n\tevent: TTSBoundaryEvent,\n): {\n\tboundary: { start: number; length: number } | null;\n\tspokenText: string;\n} => {\n\tconst tokenization = tokenizeSpeechSource({ speechText: chunk.speechText });\n\tif (!Number.isFinite(event.position)) {\n\t\treturn { boundary: null, spokenText: tokenization.spokenText };\n\t}\n\tconst resolved = resolveBoundaryToSpeechToken({\n\t\ttokenization,\n\t\tposition: event.position,\n\t\tlength: event.length,\n\t\tboundaryWord: event.word,\n\t});\n\treturn {\n\t\tboundary: resolved\n\t\t\t? { start: resolved.start, length: resolved.length }\n\t\t\t: null,\n\t\tspokenText: tokenization.spokenText,\n\t};\n};\n\nconst tokenAtOffsetMatches = (\n\tspeechText: string,\n\tstart: number,\n\tnormalizedWord: string | null,\n): boolean => {\n\tif (!normalizedWord) return true;\n\tconst token = tokenizeSpeechSource({ speechText }).tokens.find(\n\t\t(candidate) => candidate.start <= start && start < candidate.end,\n\t);\n\treturn token?.normalized === normalizedWord;\n};\n\nexport const normalizeBoundaryEvent = (\n\tchunk: TTSHighlightChunk,\n\tevent: TTSBoundaryEvent,\n): NormalizedBoundaryEvent => {\n\tconst normalizedWord = normalizeBoundaryWord(event.word);\n\tif (chunk.id !== event.chunkId) {\n\t\treturn {\n\t\t\tchunkId: event.chunkId,\n\t\t\tnormalizedWord,\n\t\t\tchunkSpokenStart: null,\n\t\t\tchunkSpokenEnd: null,\n\t\t\tconfidence: 0,\n\t\t\treason: \"boundary chunk id did not match highlight chunk\",\n\t\t};\n\t}\n\tif (\n\t\tevent.providerOffsetSpace === \"unknown\" ||\n\t\tchunk.offsetSpace === \"unsupported\"\n\t) {\n\t\treturn {\n\t\t\tchunkId: chunk.id,\n\t\t\tnormalizedWord,\n\t\t\tchunkSpokenStart: null,\n\t\t\tchunkSpokenEnd: null,\n\t\t\tconfidence: 0,\n\t\t\treason: \"boundary offset space is unsupported\",\n\t\t};\n\t}\n\tlet boundary: { start: number; length: number } | null;\n\tlet spokenText: string;\n\tif (chunk.catalogAlignment) {\n\t\tboundary = resolveSpokenBoundaryOffset(\n\t\t\tchunk.catalogAlignment,\n\t\t\tevent.position,\n\t\t\tevent.length,\n\t\t\tevent.word,\n\t\t);\n\t\tspokenText = chunk.catalogAlignment.spokenText;\n\t} else {\n\t\tconst resolved = speechSourceBoundary(chunk, event);\n\t\tboundary = resolved.boundary;\n\t\tspokenText = resolved.spokenText;\n\t}\n\tif (!boundary) {\n\t\treturn {\n\t\t\tchunkId: chunk.id,\n\t\t\tnormalizedWord,\n\t\t\tchunkSpokenStart: null,\n\t\t\tchunkSpokenEnd: null,\n\t\t\tconfidence: 0,\n\t\t\treason: \"could not resolve provider boundary to chunk speech\",\n\t\t};\n\t}\n\tif (!tokenAtOffsetMatches(spokenText, boundary.start, normalizedWord)) {\n\t\treturn {\n\t\t\tchunkId: chunk.id,\n\t\t\tnormalizedWord,\n\t\t\tchunkSpokenStart: null,\n\t\t\tchunkSpokenEnd: null,\n\t\t\tconfidence: 0,\n\t\t\treason: \"could not resolve provider boundary to matching word\",\n\t\t};\n\t}\n\treturn {\n\t\tchunkId: chunk.id,\n\t\tnormalizedWord,\n\t\tchunkSpokenStart: boundary.start,\n\t\tchunkSpokenEnd: boundary.start + boundary.length,\n\t\tconfidence: normalizedWord ? 1 : 0.75,\n\t\treason: chunk.catalogAlignment\n\t\t\t? \"resolved through catalog alignment\"\n\t\t\t: \"resolved through speech tokenization\",\n\t};\n};\n"]}