@pie-players/pie-assessment-toolkit 0.3.47 → 0.3.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/attempt/AssessmentSession.js +2 -1
  2. package/dist/attempt/AssessmentSession.js.map +1 -1
  3. package/dist/attempt/adapters/activity-to-test-attempt-session.js +12 -8
  4. package/dist/attempt/adapters/activity-to-test-attempt-session.js.map +1 -1
  5. package/dist/components/ItemToolBar.custom-element.js +56 -17
  6. package/dist/components/PieAssessmentToolkit.custom-element.js +84 -29
  7. package/dist/components/SectionToolBar.custom-element.js +55 -17
  8. package/dist/index.d.ts +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/policy/core/pnp-policy-inputs.js +1 -2
  11. package/dist/policy/core/pnp-policy-inputs.js.map +1 -1
  12. package/dist/policy/core/provenance.js.map +1 -1
  13. package/dist/runtime/SectionRuntimeEngine.js +1 -1
  14. package/dist/runtime/SectionRuntimeEngine.js.map +1 -1
  15. package/dist/runtime/core/cohort.js.map +1 -1
  16. package/dist/runtime/core/engine-input.js.map +1 -1
  17. package/dist/runtime/core/engine-readiness.js.map +1 -1
  18. package/dist/runtime/core/engine-resolver.js.map +1 -1
  19. package/dist/runtime/core/engine-transition.js +1 -3
  20. package/dist/runtime/core/engine-transition.js.map +1 -1
  21. package/dist/runtime/registration-events.js.map +1 -1
  22. package/dist/runtime/session-event-emitter-policy.js.map +1 -1
  23. package/dist/services/HighlightCoordinator.js +2 -1
  24. package/dist/services/HighlightCoordinator.js.map +1 -1
  25. package/dist/services/I18nService.d.ts +1 -1
  26. package/dist/services/I18nService.js.map +1 -1
  27. package/dist/services/SSMLExtractor.js.map +1 -1
  28. package/dist/services/TTSService.js +5 -2
  29. package/dist/services/TTSService.js.map +1 -1
  30. package/dist/services/ThemeProvider.js.map +1 -1
  31. package/dist/services/ToolRegistry.d.ts +1 -1
  32. package/dist/services/ToolRegistry.js.map +1 -1
  33. package/dist/services/ToolkitCoordinator.js +21 -11
  34. package/dist/services/ToolkitCoordinator.js.map +1 -1
  35. package/dist/services/createDefaultToolRegistry.js +4 -2
  36. package/dist/services/createDefaultToolRegistry.js.map +1 -1
  37. package/dist/services/framework-error-bus.js.map +1 -1
  38. package/dist/services/framework-error.js.map +1 -1
  39. package/dist/services/interfaces.js.map +1 -1
  40. package/dist/services/section-controller-types.js.map +1 -1
  41. package/dist/services/tool-config-defaults.js +7 -1
  42. package/dist/services/tool-config-defaults.js.map +1 -1
  43. package/dist/services/tool-config-validation.js +3 -1
  44. package/dist/services/tool-config-validation.js.map +1 -1
  45. package/dist/services/tool-instance-id.js.map +1 -1
  46. package/dist/services/tool-providers/DesmosToolProvider.js.map +1 -1
  47. package/dist/services/tool-providers/TTSToolProvider.js.map +1 -1
  48. package/dist/services/toolbar-items.js.map +1 -1
  49. package/dist/services/tools-config-normalizer.js +1 -2
  50. package/dist/services/tools-config-normalizer.js.map +1 -1
  51. package/dist/services/tts/browser-provider.js +60 -18
  52. package/dist/services/tts/browser-provider.js.map +1 -1
  53. package/dist/services/tts/catalog-span-alignment.js +8 -3
  54. package/dist/services/tts/catalog-span-alignment.js.map +1 -1
  55. package/dist/services/tts/generated-speech/assemble-plan.js.map +1 -1
  56. package/dist/services/tts/generated-speech/index.d.ts +1 -1
  57. package/dist/services/tts/generated-speech/index.js +1 -1
  58. package/dist/services/tts/generated-speech/index.js.map +1 -1
  59. package/dist/services/tts/generated-speech/math-speech-cache.js +3 -1
  60. package/dist/services/tts/generated-speech/math-speech-cache.js.map +1 -1
  61. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js.map +1 -1
  62. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js.map +1 -1
  63. package/dist/services/tts/math-alignment/mathml-tokenizer.js.map +1 -1
  64. package/dist/services/tts/math-alignment/sequence-aligner.js.map +1 -1
  65. package/dist/services/tts/math-alignment/speech-tokenizer.js.map +1 -1
  66. package/dist/services/tts/math-aware-text-processing.js +6 -3
  67. package/dist/services/tts/math-aware-text-processing.js.map +1 -1
  68. package/dist/services/tts/math-speech.js +3 -1
  69. package/dist/services/tts/math-speech.js.map +1 -1
  70. package/dist/services/tts/visual-line-ranges.js.map +1 -1
  71. package/dist/services/tts-runtime-config.js +6 -2
  72. package/dist/services/tts-runtime-config.js.map +1 -1
  73. package/dist/tools/calculators/desmos-provider.js.map +1 -1
  74. package/dist/tools/default-tool-module-loaders.js.map +1 -1
  75. package/dist/tools/registrations/accessibility-tools.js.map +1 -1
  76. package/dist/tools/registrations/calculator.js +1 -1
  77. package/dist/tools/registrations/calculator.js.map +1 -1
  78. package/dist/tools/registrations/interaction-tools.js.map +1 -1
  79. package/dist/tools/registrations/measurement-tools.js.map +1 -1
  80. package/dist/tools/registrations/subject-specific-tools.js +1 -1
  81. package/dist/tools/registrations/subject-specific-tools.js.map +1 -1
  82. package/dist/tools/registrations/tts.js +4 -2
  83. package/dist/tools/registrations/tts.js.map +1 -1
  84. package/dist/tools/types.js.map +1 -1
  85. package/dist/tools/variant-resolver.js.map +1 -1
  86. package/package.json +7 -7
@@ -14,7 +14,8 @@ export function loadAssessmentSession(storage, assessmentAttemptSessionIdentifie
14
14
  const parsed = JSON.parse(raw);
15
15
  if (!parsed || parsed.version !== ASSESSMENT_SESSION_VERSION)
16
16
  return null;
17
- if (parsed.assessmentAttemptSessionIdentifier !== assessmentAttemptSessionIdentifier) {
17
+ if (parsed.assessmentAttemptSessionIdentifier !==
18
+ assessmentAttemptSessionIdentifier) {
18
19
  return null;
19
20
  }
20
21
  return parsed;
@@ -1 +1 @@
1
- {"version":3,"file":"AssessmentSession.js","sourceRoot":"","sources":["../../src/attempt/AssessmentSession.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SectionControllerSessionState } from \"../services/section-controller-types.js\";\nimport type { StorageLike } from \"./TestSession.js\";\n\nexport interface AssessmentSessionNavigationState {\n\tcurrentSectionIndex: number;\n\tvisitedSectionIdentifiers: string[];\n\tcurrentSectionIdentifier?: string;\n}\n\nexport interface AssessmentSessionRealization {\n\tseed: string;\n\tsectionIdentifiers: string[];\n}\n\nexport interface AssessmentSectionSessionState {\n\tsectionIdentifier: string;\n\tupdatedAt: string;\n\tsession: SectionControllerSessionState | null;\n}\n\nexport interface AssessmentSession {\n\tversion: 1;\n\tassessmentAttemptSessionIdentifier: string;\n\tassessmentId: string;\n\tstartedAt: string;\n\tupdatedAt: string;\n\tcompletedAt?: string;\n\tnavigationState: AssessmentSessionNavigationState;\n\trealization: AssessmentSessionRealization;\n\tsectionSessions: Record<string, AssessmentSectionSessionState>;\n\tcontextVariables?: Record<string, unknown>;\n}\n\nconst ASSESSMENT_SESSION_VERSION = 1 as const;\nconst STORAGE_PREFIX = \"pie:assessment-session:v1:\";\n\nfunction nowIso(): string {\n\treturn new Date().toISOString();\n}\n\nexport function getAssessmentSessionStorageKey(\n\tassessmentAttemptSessionIdentifier: string,\n): string {\n\treturn `${STORAGE_PREFIX}${assessmentAttemptSessionIdentifier}`;\n}\n\nexport function loadAssessmentSession(\n\tstorage: StorageLike,\n\tassessmentAttemptSessionIdentifier: string,\n): AssessmentSession | null {\n\tconst raw = storage.getItem(\n\t\tgetAssessmentSessionStorageKey(assessmentAttemptSessionIdentifier),\n\t);\n\tif (!raw) return null;\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as AssessmentSession;\n\t\tif (!parsed || parsed.version !== ASSESSMENT_SESSION_VERSION) return null;\n\t\tif (\n\t\t\tparsed.assessmentAttemptSessionIdentifier !== assessmentAttemptSessionIdentifier\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function saveAssessmentSession(\n\tstorage: StorageLike,\n\tsession: AssessmentSession,\n): void {\n\tconst updated: AssessmentSession = {\n\t\t...session,\n\t\tupdatedAt: nowIso(),\n\t};\n\tstorage.setItem(\n\t\tgetAssessmentSessionStorageKey(session.assessmentAttemptSessionIdentifier),\n\t\tJSON.stringify(updated),\n\t);\n}\n\nexport function createNewAssessmentSession(args: {\n\tassessmentAttemptSessionIdentifier: string;\n\tassessmentId: string;\n\tseed: string;\n\tsectionIdentifiers: string[];\n}): AssessmentSession {\n\tconst startedAt = nowIso();\n\tconst initialSectionIdentifier = args.sectionIdentifiers[0];\n\treturn {\n\t\tversion: ASSESSMENT_SESSION_VERSION,\n\t\tassessmentAttemptSessionIdentifier: args.assessmentAttemptSessionIdentifier,\n\t\tassessmentId: args.assessmentId,\n\t\tstartedAt,\n\t\tupdatedAt: startedAt,\n\t\tnavigationState: {\n\t\t\tcurrentSectionIndex: 0,\n\t\t\tvisitedSectionIdentifiers: initialSectionIdentifier\n\t\t\t\t? [initialSectionIdentifier]\n\t\t\t\t: [],\n\t\t\tcurrentSectionIdentifier: initialSectionIdentifier,\n\t\t},\n\t\trealization: {\n\t\t\tseed: args.seed,\n\t\t\tsectionIdentifiers: args.sectionIdentifiers,\n\t\t},\n\t\tsectionSessions: {},\n\t};\n}\n\nexport function upsertSectionSession(\n\tsession: AssessmentSession,\n\targs: {\n\t\tsectionIdentifier: string;\n\t\tsectionSession: SectionControllerSessionState | null;\n\t},\n): AssessmentSession {\n\tif (!args.sectionIdentifier) return session;\n\tconst updatedAt = nowIso();\n\treturn {\n\t\t...session,\n\t\tupdatedAt,\n\t\tsectionSessions: {\n\t\t\t...session.sectionSessions,\n\t\t\t[args.sectionIdentifier]: {\n\t\t\t\tsectionIdentifier: args.sectionIdentifier,\n\t\t\t\tupdatedAt,\n\t\t\t\tsession: args.sectionSession,\n\t\t\t},\n\t\t},\n\t};\n}\n\nexport function setCurrentSectionPosition(\n\tsession: AssessmentSession,\n\targs: {\n\t\tcurrentSectionIndex: number;\n\t\tcurrentSectionIdentifier?: string;\n\t},\n): AssessmentSession {\n\tconst visited = new Set(session.navigationState.visitedSectionIdentifiers || []);\n\tif (args.currentSectionIdentifier) {\n\t\tvisited.add(args.currentSectionIdentifier);\n\t}\n\treturn {\n\t\t...session,\n\t\tnavigationState: {\n\t\t\t...session.navigationState,\n\t\t\tcurrentSectionIndex: args.currentSectionIndex,\n\t\t\tcurrentSectionIdentifier: args.currentSectionIdentifier,\n\t\t\tvisitedSectionIdentifiers: Array.from(visited),\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"AssessmentSession.js","sourceRoot":"","sources":["../../src/attempt/AssessmentSession.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SectionControllerSessionState } from \"../services/section-controller-types.js\";\nimport type { StorageLike } from \"./TestSession.js\";\n\nexport interface AssessmentSessionNavigationState {\n\tcurrentSectionIndex: number;\n\tvisitedSectionIdentifiers: string[];\n\tcurrentSectionIdentifier?: string;\n}\n\nexport interface AssessmentSessionRealization {\n\tseed: string;\n\tsectionIdentifiers: string[];\n}\n\nexport interface AssessmentSectionSessionState {\n\tsectionIdentifier: string;\n\tupdatedAt: string;\n\tsession: SectionControllerSessionState | null;\n}\n\nexport interface AssessmentSession {\n\tversion: 1;\n\tassessmentAttemptSessionIdentifier: string;\n\tassessmentId: string;\n\tstartedAt: string;\n\tupdatedAt: string;\n\tcompletedAt?: string;\n\tnavigationState: AssessmentSessionNavigationState;\n\trealization: AssessmentSessionRealization;\n\tsectionSessions: Record<string, AssessmentSectionSessionState>;\n\tcontextVariables?: Record<string, unknown>;\n}\n\nconst ASSESSMENT_SESSION_VERSION = 1 as const;\nconst STORAGE_PREFIX = \"pie:assessment-session:v1:\";\n\nfunction nowIso(): string {\n\treturn new Date().toISOString();\n}\n\nexport function getAssessmentSessionStorageKey(\n\tassessmentAttemptSessionIdentifier: string,\n): string {\n\treturn `${STORAGE_PREFIX}${assessmentAttemptSessionIdentifier}`;\n}\n\nexport function loadAssessmentSession(\n\tstorage: StorageLike,\n\tassessmentAttemptSessionIdentifier: string,\n): AssessmentSession | null {\n\tconst raw = storage.getItem(\n\t\tgetAssessmentSessionStorageKey(assessmentAttemptSessionIdentifier),\n\t);\n\tif (!raw) return null;\n\ttry {\n\t\tconst parsed = JSON.parse(raw) as AssessmentSession;\n\t\tif (!parsed || parsed.version !== ASSESSMENT_SESSION_VERSION) return null;\n\t\tif (\n\t\t\tparsed.assessmentAttemptSessionIdentifier !==\n\t\t\tassessmentAttemptSessionIdentifier\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function saveAssessmentSession(\n\tstorage: StorageLike,\n\tsession: AssessmentSession,\n): void {\n\tconst updated: AssessmentSession = {\n\t\t...session,\n\t\tupdatedAt: nowIso(),\n\t};\n\tstorage.setItem(\n\t\tgetAssessmentSessionStorageKey(session.assessmentAttemptSessionIdentifier),\n\t\tJSON.stringify(updated),\n\t);\n}\n\nexport function createNewAssessmentSession(args: {\n\tassessmentAttemptSessionIdentifier: string;\n\tassessmentId: string;\n\tseed: string;\n\tsectionIdentifiers: string[];\n}): AssessmentSession {\n\tconst startedAt = nowIso();\n\tconst initialSectionIdentifier = args.sectionIdentifiers[0];\n\treturn {\n\t\tversion: ASSESSMENT_SESSION_VERSION,\n\t\tassessmentAttemptSessionIdentifier: args.assessmentAttemptSessionIdentifier,\n\t\tassessmentId: args.assessmentId,\n\t\tstartedAt,\n\t\tupdatedAt: startedAt,\n\t\tnavigationState: {\n\t\t\tcurrentSectionIndex: 0,\n\t\t\tvisitedSectionIdentifiers: initialSectionIdentifier\n\t\t\t\t? [initialSectionIdentifier]\n\t\t\t\t: [],\n\t\t\tcurrentSectionIdentifier: initialSectionIdentifier,\n\t\t},\n\t\trealization: {\n\t\t\tseed: args.seed,\n\t\t\tsectionIdentifiers: args.sectionIdentifiers,\n\t\t},\n\t\tsectionSessions: {},\n\t};\n}\n\nexport function upsertSectionSession(\n\tsession: AssessmentSession,\n\targs: {\n\t\tsectionIdentifier: string;\n\t\tsectionSession: SectionControllerSessionState | null;\n\t},\n): AssessmentSession {\n\tif (!args.sectionIdentifier) return session;\n\tconst updatedAt = nowIso();\n\treturn {\n\t\t...session,\n\t\tupdatedAt,\n\t\tsectionSessions: {\n\t\t\t...session.sectionSessions,\n\t\t\t[args.sectionIdentifier]: {\n\t\t\t\tsectionIdentifier: args.sectionIdentifier,\n\t\t\t\tupdatedAt,\n\t\t\t\tsession: args.sectionSession,\n\t\t\t},\n\t\t},\n\t};\n}\n\nexport function setCurrentSectionPosition(\n\tsession: AssessmentSession,\n\targs: {\n\t\tcurrentSectionIndex: number;\n\t\tcurrentSectionIdentifier?: string;\n\t},\n): AssessmentSession {\n\tconst visited = new Set(\n\t\tsession.navigationState.visitedSectionIdentifiers || [],\n\t);\n\tif (args.currentSectionIdentifier) {\n\t\tvisited.add(args.currentSectionIdentifier);\n\t}\n\treturn {\n\t\t...session,\n\t\tnavigationState: {\n\t\t\t...session.navigationState,\n\t\t\tcurrentSectionIndex: args.currentSectionIndex,\n\t\t\tcurrentSectionIdentifier: args.currentSectionIdentifier,\n\t\t\tvisitedSectionIdentifiers: Array.from(visited),\n\t\t},\n\t};\n}\n"]}
@@ -2,10 +2,7 @@ function nowIso() {
2
2
  return new Date().toISOString();
3
3
  }
4
4
  function getItemIdentifier(itemRef) {
5
- return (itemRef.identifier ||
6
- itemRef.item?.identifier ||
7
- itemRef.item?.id ||
8
- null);
5
+ return (itemRef.identifier || itemRef.item?.identifier || itemRef.item?.id || null);
9
6
  }
10
7
  function resolveItemIdentifiers(args) {
11
8
  const refs = args.itemRefs || args.activityDefinition?.assessmentItemRefs || [];
@@ -39,7 +36,8 @@ function mapItemSessions(itemIdentifiers, backendItemSessions) {
39
36
  if (!rawSession)
40
37
  return acc;
41
38
  const pieSessionId = typeof rawSession.id === "string" ? rawSession.id : undefined;
42
- const attemptCount = typeof rawSession.attemptCount === "number" && rawSession.attemptCount > 0
39
+ const attemptCount = typeof rawSession.attemptCount === "number" &&
40
+ rawSession.attemptCount > 0
43
41
  ? rawSession.attemptCount
44
42
  : pieSessionId
45
43
  ? 1
@@ -47,8 +45,12 @@ function mapItemSessions(itemIdentifiers, backendItemSessions) {
47
45
  const isCompleted = typeof rawSession.isCompleted === "boolean"
48
46
  ? rawSession.isCompleted
49
47
  : Boolean(rawSession.complete);
50
- const startedAt = typeof rawSession.startedAt === "string" ? rawSession.startedAt : undefined;
51
- const updatedAt = typeof rawSession.updatedAt === "string" ? rawSession.updatedAt : undefined;
48
+ const startedAt = typeof rawSession.startedAt === "string"
49
+ ? rawSession.startedAt
50
+ : undefined;
51
+ const updatedAt = typeof rawSession.updatedAt === "string"
52
+ ? rawSession.updatedAt
53
+ : undefined;
52
54
  const completedAt = typeof rawSession.completedAt === "string"
53
55
  ? rawSession.completedAt
54
56
  : undefined;
@@ -128,7 +130,9 @@ export function buildActivitySessionItemUpdate(testAttemptSession, itemIdentifie
128
130
  visitedItemIdentifiers: [
129
131
  ...testAttemptSession.navigationState.visitedItemIdentifiers,
130
132
  ],
131
- itemSessions: itemSession?.session ? { [itemIdentifier]: itemSession.session } : {},
133
+ itemSessions: itemSession?.session
134
+ ? { [itemIdentifier]: itemSession.session }
135
+ : {},
132
136
  },
133
137
  };
134
138
  }
@@ -1 +1 @@
1
- {"version":3,"file":"activity-to-test-attempt-session.js","sourceRoot":"","sources":["../../../src/attempt/adapters/activity-to-test-attempt-session.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tTestAttemptItemSession,\n\tTestAttemptSession,\n} from \"../TestSession.js\";\n\nexport interface PieBackendActivityItemRef {\n\tidentifier?: string;\n\titem?: {\n\t\tid?: string;\n\t\tidentifier?: string;\n\t};\n}\n\nexport interface PieBackendActivityDefinition {\n\tid?: string;\n\tidentifier?: string;\n\tassessmentId?: string;\n\tassessmentItemRefs?: PieBackendActivityItemRef[];\n}\n\nexport interface PieBackendActivitySession {\n\tid?: string;\n\tactivitySessionId?: string;\n\tstartedAt?: string;\n\tupdatedAt?: string;\n\tcompletedAt?: string;\n\tcurrentItemIndex?: number;\n\tcurrentSectionIdentifier?: string;\n\tvisitedItemIdentifiers?: string[];\n\titemSessions?: Record<string, unknown>;\n}\n\nexport interface MapActivityToTestAttemptSessionArgs {\n\tactivityDefinition?: PieBackendActivityDefinition | null;\n\tactivitySession?: PieBackendActivitySession | null;\n\titemRefs?: PieBackendActivityItemRef[];\n\titemSessionsByItemIdentifier?: Record<string, unknown>;\n\tassessmentId?: string;\n\ttestAttemptSessionIdentifier?: string;\n}\n\nexport interface ActivitySessionPatchPayload {\n\tactivitySession: {\n\t\tid: string;\n\t\tstartedAt?: string;\n\t\tupdatedAt?: string;\n\t\tcompletedAt?: string;\n\t\tcurrentItemIndex: number;\n\t\tcurrentSectionIdentifier?: string;\n\t\tvisitedItemIdentifiers: string[];\n\t\titemSessions: Record<string, unknown>;\n\t};\n}\n\nfunction nowIso(): string {\n\treturn new Date().toISOString();\n}\n\nfunction getItemIdentifier(itemRef: PieBackendActivityItemRef): string | null {\n\treturn (\n\t\titemRef.identifier ||\n\t\titemRef.item?.identifier ||\n\t\titemRef.item?.id ||\n\t\tnull\n\t);\n}\n\nfunction resolveItemIdentifiers(args: MapActivityToTestAttemptSessionArgs): string[] {\n\tconst refs = args.itemRefs || args.activityDefinition?.assessmentItemRefs || [];\n\tconst identifiers = refs\n\t\t.map((itemRef) => getItemIdentifier(itemRef))\n\t\t.filter((itemIdentifier): itemIdentifier is string => !!itemIdentifier);\n\treturn Array.from(new Set(identifiers));\n}\n\nfunction resolveAssessmentId(args: MapActivityToTestAttemptSessionArgs): string {\n\treturn (\n\t\targs.assessmentId ||\n\t\targs.activityDefinition?.assessmentId ||\n\t\targs.activityDefinition?.identifier ||\n\t\targs.activityDefinition?.id ||\n\t\t\"unknown-assessment\"\n\t);\n}\n\nfunction resolveTestAttemptSessionIdentifier(\n\targs: MapActivityToTestAttemptSessionArgs,\n\tassessmentId: string,\n): string {\n\treturn (\n\t\targs.testAttemptSessionIdentifier ||\n\t\targs.activitySession?.activitySessionId ||\n\t\targs.activitySession?.id ||\n\t\t`tas_${assessmentId}`\n\t);\n}\n\nfunction resolveBackendItemSessions(\n\targs: MapActivityToTestAttemptSessionArgs,\n): Record<string, unknown> {\n\treturn {\n\t\t...(args.activitySession?.itemSessions || {}),\n\t\t...(args.itemSessionsByItemIdentifier || {}),\n\t};\n}\n\nfunction mapItemSessions(\n\titemIdentifiers: string[],\n\tbackendItemSessions: Record<string, unknown>,\n): Record<string, TestAttemptItemSession> {\n\treturn itemIdentifiers.reduce<Record<string, TestAttemptItemSession>>(\n\t\t(acc, itemIdentifier) => {\n\t\t\tconst rawSession = backendItemSessions[itemIdentifier] as\n\t\t\t\t| Record<string, unknown>\n\t\t\t\t| undefined;\n\t\t\tif (!rawSession) return acc;\n\n\t\t\tconst pieSessionId =\n\t\t\t\ttypeof rawSession.id === \"string\" ? rawSession.id : undefined;\n\t\t\tconst attemptCount =\n\t\t\t\ttypeof rawSession.attemptCount === \"number\" && rawSession.attemptCount > 0\n\t\t\t\t\t? rawSession.attemptCount\n\t\t\t\t\t: pieSessionId\n\t\t\t\t\t\t? 1\n\t\t\t\t\t\t: 0;\n\t\t\tconst isCompleted =\n\t\t\t\ttypeof rawSession.isCompleted === \"boolean\"\n\t\t\t\t\t? rawSession.isCompleted\n\t\t\t\t\t: Boolean(rawSession.complete);\n\t\t\tconst startedAt =\n\t\t\t\ttypeof rawSession.startedAt === \"string\" ? rawSession.startedAt : undefined;\n\t\t\tconst updatedAt =\n\t\t\t\ttypeof rawSession.updatedAt === \"string\" ? rawSession.updatedAt : undefined;\n\t\t\tconst completedAt =\n\t\t\t\ttypeof rawSession.completedAt === \"string\"\n\t\t\t\t\t? rawSession.completedAt\n\t\t\t\t\t: undefined;\n\n\t\t\tacc[itemIdentifier] = {\n\t\t\t\titemIdentifier,\n\t\t\t\tpieSessionId,\n\t\t\t\tattemptCount,\n\t\t\t\tisCompleted,\n\t\t\t\tstartedAt,\n\t\t\t\tupdatedAt,\n\t\t\t\tcompletedAt,\n\t\t\t\tsession: rawSession,\n\t\t\t};\n\t\t\treturn acc;\n\t\t},\n\t\t{},\n\t);\n}\n\nexport function mapActivityToTestAttemptSession(\n\targs: MapActivityToTestAttemptSessionArgs,\n): TestAttemptSession {\n\tconst assessmentId = resolveAssessmentId(args);\n\tconst itemIdentifiers = resolveItemIdentifiers(args);\n\tconst testAttemptSessionIdentifier = resolveTestAttemptSessionIdentifier(\n\t\targs,\n\t\tassessmentId,\n\t);\n\tconst backendItemSessions = resolveBackendItemSessions(args);\n\tconst now = nowIso();\n\tconst startedAt = args.activitySession?.startedAt || now;\n\tconst updatedAt = args.activitySession?.updatedAt || startedAt;\n\n\treturn {\n\t\tversion: 1,\n\t\ttestAttemptSessionIdentifier,\n\t\tassessmentId,\n\t\tstartedAt,\n\t\tupdatedAt,\n\t\tcompletedAt: args.activitySession?.completedAt,\n\t\tnavigationState: {\n\t\t\tcurrentItemIndex: args.activitySession?.currentItemIndex ?? -1,\n\t\t\tvisitedItemIdentifiers: [\n\t\t\t\t...(args.activitySession?.visitedItemIdentifiers || []),\n\t\t\t],\n\t\t\tcurrentSectionIdentifier: args.activitySession?.currentSectionIdentifier,\n\t\t},\n\t\trealization: {\n\t\t\tseed: testAttemptSessionIdentifier,\n\t\t\titemIdentifiers,\n\t\t},\n\t\titemSessions: mapItemSessions(itemIdentifiers, backendItemSessions),\n\t};\n}\n\nexport function toItemSessionsRecord(\n\ttestAttemptSession: TestAttemptSession,\n): Record<string, unknown> {\n\treturn Object.entries(testAttemptSession.itemSessions).reduce<\n\t\tRecord<string, unknown>\n\t>((acc, [itemIdentifier, itemSession]) => {\n\t\tif (itemSession.session) {\n\t\t\tacc[itemIdentifier] = itemSession.session;\n\t\t}\n\t\treturn acc;\n\t}, {});\n}\n\nexport function buildActivitySessionPatchFromTestAttemptSession(\n\ttestAttemptSession: TestAttemptSession,\n): ActivitySessionPatchPayload {\n\treturn {\n\t\tactivitySession: {\n\t\t\tid: testAttemptSession.testAttemptSessionIdentifier,\n\t\t\tstartedAt: testAttemptSession.startedAt,\n\t\t\tupdatedAt: testAttemptSession.updatedAt,\n\t\t\tcompletedAt: testAttemptSession.completedAt,\n\t\t\tcurrentItemIndex: testAttemptSession.navigationState.currentItemIndex,\n\t\t\tcurrentSectionIdentifier:\n\t\t\t\ttestAttemptSession.navigationState.currentSectionIdentifier,\n\t\t\tvisitedItemIdentifiers: [\n\t\t\t\t...testAttemptSession.navigationState.visitedItemIdentifiers,\n\t\t\t],\n\t\t\titemSessions: toItemSessionsRecord(testAttemptSession),\n\t\t},\n\t};\n}\n\nexport function buildActivitySessionItemUpdate(\n\ttestAttemptSession: TestAttemptSession,\n\titemIdentifier: string,\n): ActivitySessionPatchPayload {\n\tconst itemSession = testAttemptSession.itemSessions[itemIdentifier];\n\treturn {\n\t\tactivitySession: {\n\t\t\tid: testAttemptSession.testAttemptSessionIdentifier,\n\t\t\tcurrentItemIndex: testAttemptSession.navigationState.currentItemIndex,\n\t\t\tcurrentSectionIdentifier:\n\t\t\t\ttestAttemptSession.navigationState.currentSectionIdentifier,\n\t\t\tvisitedItemIdentifiers: [\n\t\t\t\t...testAttemptSession.navigationState.visitedItemIdentifiers,\n\t\t\t],\n\t\t\titemSessions: itemSession?.session ? { [itemIdentifier]: itemSession.session } : {},\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"activity-to-test-attempt-session.js","sourceRoot":"","sources":["../../../src/attempt/adapters/activity-to-test-attempt-session.ts"],"names":[],"mappings":"AAsDA,SAAS,MAAM;IACd,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AACjC,CAAC;AAED,SAAS,iBAAiB,CAAC,OAAkC;IAC5D,OAAO,CACN,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,IAAI,EAAE,UAAU,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,IAAI,IAAI,CAC1E,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB,CAC9B,IAAyC;IAEzC,MAAM,IAAI,GACT,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,kBAAkB,EAAE,kBAAkB,IAAI,EAAE,CAAC;IACpE,MAAM,WAAW,GAAG,IAAI;SACtB,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;SAC5C,MAAM,CAAC,CAAC,cAAc,EAA4B,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IACzE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,mBAAmB,CAC3B,IAAyC;IAEzC,OAAO,CACN,IAAI,CAAC,YAAY;QACjB,IAAI,CAAC,kBAAkB,EAAE,YAAY;QACrC,IAAI,CAAC,kBAAkB,EAAE,UAAU;QACnC,IAAI,CAAC,kBAAkB,EAAE,EAAE;QAC3B,oBAAoB,CACpB,CAAC;AACH,CAAC;AAED,SAAS,mCAAmC,CAC3C,IAAyC,EACzC,YAAoB;IAEpB,OAAO,CACN,IAAI,CAAC,4BAA4B;QACjC,IAAI,CAAC,eAAe,EAAE,iBAAiB;QACvC,IAAI,CAAC,eAAe,EAAE,EAAE;QACxB,OAAO,YAAY,EAAE,CACrB,CAAC;AACH,CAAC;AAED,SAAS,0BAA0B,CAClC,IAAyC;IAEzC,OAAO;QACN,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,YAAY,IAAI,EAAE,CAAC;QAC7C,GAAG,CAAC,IAAI,CAAC,4BAA4B,IAAI,EAAE,CAAC;KAC5C,CAAC;AACH,CAAC;AAED,SAAS,eAAe,CACvB,eAAyB,EACzB,mBAA4C;IAE5C,OAAO,eAAe,CAAC,MAAM,CAC5B,CAAC,GAAG,EAAE,cAAc,EAAE,EAAE;QACvB,MAAM,UAAU,GAAG,mBAAmB,CAAC,cAAc,CAEzC,CAAC;QACb,IAAI,CAAC,UAAU;YAAE,OAAO,GAAG,CAAC;QAE5B,MAAM,YAAY,GACjB,OAAO,UAAU,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/D,MAAM,YAAY,GACjB,OAAO,UAAU,CAAC,YAAY,KAAK,QAAQ;YAC3C,UAAU,CAAC,YAAY,GAAG,CAAC;YAC1B,CAAC,CAAC,UAAU,CAAC,YAAY;YACzB,CAAC,CAAC,YAAY;gBACb,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC,CAAC,CAAC;QACP,MAAM,WAAW,GAChB,OAAO,UAAU,CAAC,WAAW,KAAK,SAAS;YAC1C,CAAC,CAAC,UAAU,CAAC,WAAW;YACxB,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACjC,MAAM,SAAS,GACd,OAAO,UAAU,CAAC,SAAS,KAAK,QAAQ;YACvC,CAAC,CAAC,UAAU,CAAC,SAAS;YACtB,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,SAAS,GACd,OAAO,UAAU,CAAC,SAAS,KAAK,QAAQ;YACvC,CAAC,CAAC,UAAU,CAAC,SAAS;YACtB,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,WAAW,GAChB,OAAO,UAAU,CAAC,WAAW,KAAK,QAAQ;YACzC,CAAC,CAAC,UAAU,CAAC,WAAW;YACxB,CAAC,CAAC,SAAS,CAAC;QAEd,GAAG,CAAC,cAAc,CAAC,GAAG;YACrB,cAAc;YACd,YAAY;YACZ,YAAY;YACZ,WAAW;YACX,SAAS;YACT,SAAS;YACT,WAAW;YACX,OAAO,EAAE,UAAU;SACnB,CAAC;QACF,OAAO,GAAG,CAAC;IACZ,CAAC,EACD,EAAE,CACF,CAAC;AACH,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC9C,IAAyC;IAEzC,MAAM,YAAY,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAC/C,MAAM,eAAe,GAAG,sBAAsB,CAAC,IAAI,CAAC,CAAC;IACrD,MAAM,4BAA4B,GAAG,mCAAmC,CACvE,IAAI,EACJ,YAAY,CACZ,CAAC;IACF,MAAM,mBAAmB,GAAG,0BAA0B,CAAC,IAAI,CAAC,CAAC;IAC7D,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC;IACrB,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,EAAE,SAAS,IAAI,GAAG,CAAC;IACzD,MAAM,SAAS,GAAG,IAAI,CAAC,eAAe,EAAE,SAAS,IAAI,SAAS,CAAC;IAE/D,OAAO;QACN,OAAO,EAAE,CAAC;QACV,4BAA4B;QAC5B,YAAY;QACZ,SAAS;QACT,SAAS;QACT,WAAW,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW;QAC9C,eAAe,EAAE;YAChB,gBAAgB,EAAE,IAAI,CAAC,eAAe,EAAE,gBAAgB,IAAI,CAAC,CAAC;YAC9D,sBAAsB,EAAE;gBACvB,GAAG,CAAC,IAAI,CAAC,eAAe,EAAE,sBAAsB,IAAI,EAAE,CAAC;aACvD;YACD,wBAAwB,EAAE,IAAI,CAAC,eAAe,EAAE,wBAAwB;SACxE;QACD,WAAW,EAAE;YACZ,IAAI,EAAE,4BAA4B;YAClC,eAAe;SACf;QACD,YAAY,EAAE,eAAe,CAAC,eAAe,EAAE,mBAAmB,CAAC;KACnE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CACnC,kBAAsC;IAEtC,OAAO,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC,MAAM,CAE3D,CAAC,GAAG,EAAE,CAAC,cAAc,EAAE,WAAW,CAAC,EAAE,EAAE;QACxC,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;YACzB,GAAG,CAAC,cAAc,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC;QAC3C,CAAC;QACD,OAAO,GAAG,CAAC;IACZ,CAAC,EAAE,EAAE,CAAC,CAAC;AACR,CAAC;AAED,MAAM,UAAU,+CAA+C,CAC9D,kBAAsC;IAEtC,OAAO;QACN,eAAe,EAAE;YAChB,EAAE,EAAE,kBAAkB,CAAC,4BAA4B;YACnD,SAAS,EAAE,kBAAkB,CAAC,SAAS;YACvC,SAAS,EAAE,kBAAkB,CAAC,SAAS;YACvC,WAAW,EAAE,kBAAkB,CAAC,WAAW;YAC3C,gBAAgB,EAAE,kBAAkB,CAAC,eAAe,CAAC,gBAAgB;YACrE,wBAAwB,EACvB,kBAAkB,CAAC,eAAe,CAAC,wBAAwB;YAC5D,sBAAsB,EAAE;gBACvB,GAAG,kBAAkB,CAAC,eAAe,CAAC,sBAAsB;aAC5D;YACD,YAAY,EAAE,oBAAoB,CAAC,kBAAkB,CAAC;SACtD;KACD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,8BAA8B,CAC7C,kBAAsC,EACtC,cAAsB;IAEtB,MAAM,WAAW,GAAG,kBAAkB,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;IACpE,OAAO;QACN,eAAe,EAAE;YAChB,EAAE,EAAE,kBAAkB,CAAC,4BAA4B;YACnD,gBAAgB,EAAE,kBAAkB,CAAC,eAAe,CAAC,gBAAgB;YACrE,wBAAwB,EACvB,kBAAkB,CAAC,eAAe,CAAC,wBAAwB;YAC5D,sBAAsB,EAAE;gBACvB,GAAG,kBAAkB,CAAC,eAAe,CAAC,sBAAsB;aAC5D;YACD,YAAY,EAAE,WAAW,EAAE,OAAO;gBACjC,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC,EAAE,WAAW,CAAC,OAAO,EAAE;gBAC3C,CAAC,CAAC,EAAE;SACL;KACD,CAAC;AACH,CAAC","sourcesContent":["import type {\n\tTestAttemptItemSession,\n\tTestAttemptSession,\n} from \"../TestSession.js\";\n\nexport interface PieBackendActivityItemRef {\n\tidentifier?: string;\n\titem?: {\n\t\tid?: string;\n\t\tidentifier?: string;\n\t};\n}\n\nexport interface PieBackendActivityDefinition {\n\tid?: string;\n\tidentifier?: string;\n\tassessmentId?: string;\n\tassessmentItemRefs?: PieBackendActivityItemRef[];\n}\n\nexport interface PieBackendActivitySession {\n\tid?: string;\n\tactivitySessionId?: string;\n\tstartedAt?: string;\n\tupdatedAt?: string;\n\tcompletedAt?: string;\n\tcurrentItemIndex?: number;\n\tcurrentSectionIdentifier?: string;\n\tvisitedItemIdentifiers?: string[];\n\titemSessions?: Record<string, unknown>;\n}\n\nexport interface MapActivityToTestAttemptSessionArgs {\n\tactivityDefinition?: PieBackendActivityDefinition | null;\n\tactivitySession?: PieBackendActivitySession | null;\n\titemRefs?: PieBackendActivityItemRef[];\n\titemSessionsByItemIdentifier?: Record<string, unknown>;\n\tassessmentId?: string;\n\ttestAttemptSessionIdentifier?: string;\n}\n\nexport interface ActivitySessionPatchPayload {\n\tactivitySession: {\n\t\tid: string;\n\t\tstartedAt?: string;\n\t\tupdatedAt?: string;\n\t\tcompletedAt?: string;\n\t\tcurrentItemIndex: number;\n\t\tcurrentSectionIdentifier?: string;\n\t\tvisitedItemIdentifiers: string[];\n\t\titemSessions: Record<string, unknown>;\n\t};\n}\n\nfunction nowIso(): string {\n\treturn new Date().toISOString();\n}\n\nfunction getItemIdentifier(itemRef: PieBackendActivityItemRef): string | null {\n\treturn (\n\t\titemRef.identifier || itemRef.item?.identifier || itemRef.item?.id || null\n\t);\n}\n\nfunction resolveItemIdentifiers(\n\targs: MapActivityToTestAttemptSessionArgs,\n): string[] {\n\tconst refs =\n\t\targs.itemRefs || args.activityDefinition?.assessmentItemRefs || [];\n\tconst identifiers = refs\n\t\t.map((itemRef) => getItemIdentifier(itemRef))\n\t\t.filter((itemIdentifier): itemIdentifier is string => !!itemIdentifier);\n\treturn Array.from(new Set(identifiers));\n}\n\nfunction resolveAssessmentId(\n\targs: MapActivityToTestAttemptSessionArgs,\n): string {\n\treturn (\n\t\targs.assessmentId ||\n\t\targs.activityDefinition?.assessmentId ||\n\t\targs.activityDefinition?.identifier ||\n\t\targs.activityDefinition?.id ||\n\t\t\"unknown-assessment\"\n\t);\n}\n\nfunction resolveTestAttemptSessionIdentifier(\n\targs: MapActivityToTestAttemptSessionArgs,\n\tassessmentId: string,\n): string {\n\treturn (\n\t\targs.testAttemptSessionIdentifier ||\n\t\targs.activitySession?.activitySessionId ||\n\t\targs.activitySession?.id ||\n\t\t`tas_${assessmentId}`\n\t);\n}\n\nfunction resolveBackendItemSessions(\n\targs: MapActivityToTestAttemptSessionArgs,\n): Record<string, unknown> {\n\treturn {\n\t\t...(args.activitySession?.itemSessions || {}),\n\t\t...(args.itemSessionsByItemIdentifier || {}),\n\t};\n}\n\nfunction mapItemSessions(\n\titemIdentifiers: string[],\n\tbackendItemSessions: Record<string, unknown>,\n): Record<string, TestAttemptItemSession> {\n\treturn itemIdentifiers.reduce<Record<string, TestAttemptItemSession>>(\n\t\t(acc, itemIdentifier) => {\n\t\t\tconst rawSession = backendItemSessions[itemIdentifier] as\n\t\t\t\t| Record<string, unknown>\n\t\t\t\t| undefined;\n\t\t\tif (!rawSession) return acc;\n\n\t\t\tconst pieSessionId =\n\t\t\t\ttypeof rawSession.id === \"string\" ? rawSession.id : undefined;\n\t\t\tconst attemptCount =\n\t\t\t\ttypeof rawSession.attemptCount === \"number\" &&\n\t\t\t\trawSession.attemptCount > 0\n\t\t\t\t\t? rawSession.attemptCount\n\t\t\t\t\t: pieSessionId\n\t\t\t\t\t\t? 1\n\t\t\t\t\t\t: 0;\n\t\t\tconst isCompleted =\n\t\t\t\ttypeof rawSession.isCompleted === \"boolean\"\n\t\t\t\t\t? rawSession.isCompleted\n\t\t\t\t\t: Boolean(rawSession.complete);\n\t\t\tconst startedAt =\n\t\t\t\ttypeof rawSession.startedAt === \"string\"\n\t\t\t\t\t? rawSession.startedAt\n\t\t\t\t\t: undefined;\n\t\t\tconst updatedAt =\n\t\t\t\ttypeof rawSession.updatedAt === \"string\"\n\t\t\t\t\t? rawSession.updatedAt\n\t\t\t\t\t: undefined;\n\t\t\tconst completedAt =\n\t\t\t\ttypeof rawSession.completedAt === \"string\"\n\t\t\t\t\t? rawSession.completedAt\n\t\t\t\t\t: undefined;\n\n\t\t\tacc[itemIdentifier] = {\n\t\t\t\titemIdentifier,\n\t\t\t\tpieSessionId,\n\t\t\t\tattemptCount,\n\t\t\t\tisCompleted,\n\t\t\t\tstartedAt,\n\t\t\t\tupdatedAt,\n\t\t\t\tcompletedAt,\n\t\t\t\tsession: rawSession,\n\t\t\t};\n\t\t\treturn acc;\n\t\t},\n\t\t{},\n\t);\n}\n\nexport function mapActivityToTestAttemptSession(\n\targs: MapActivityToTestAttemptSessionArgs,\n): TestAttemptSession {\n\tconst assessmentId = resolveAssessmentId(args);\n\tconst itemIdentifiers = resolveItemIdentifiers(args);\n\tconst testAttemptSessionIdentifier = resolveTestAttemptSessionIdentifier(\n\t\targs,\n\t\tassessmentId,\n\t);\n\tconst backendItemSessions = resolveBackendItemSessions(args);\n\tconst now = nowIso();\n\tconst startedAt = args.activitySession?.startedAt || now;\n\tconst updatedAt = args.activitySession?.updatedAt || startedAt;\n\n\treturn {\n\t\tversion: 1,\n\t\ttestAttemptSessionIdentifier,\n\t\tassessmentId,\n\t\tstartedAt,\n\t\tupdatedAt,\n\t\tcompletedAt: args.activitySession?.completedAt,\n\t\tnavigationState: {\n\t\t\tcurrentItemIndex: args.activitySession?.currentItemIndex ?? -1,\n\t\t\tvisitedItemIdentifiers: [\n\t\t\t\t...(args.activitySession?.visitedItemIdentifiers || []),\n\t\t\t],\n\t\t\tcurrentSectionIdentifier: args.activitySession?.currentSectionIdentifier,\n\t\t},\n\t\trealization: {\n\t\t\tseed: testAttemptSessionIdentifier,\n\t\t\titemIdentifiers,\n\t\t},\n\t\titemSessions: mapItemSessions(itemIdentifiers, backendItemSessions),\n\t};\n}\n\nexport function toItemSessionsRecord(\n\ttestAttemptSession: TestAttemptSession,\n): Record<string, unknown> {\n\treturn Object.entries(testAttemptSession.itemSessions).reduce<\n\t\tRecord<string, unknown>\n\t>((acc, [itemIdentifier, itemSession]) => {\n\t\tif (itemSession.session) {\n\t\t\tacc[itemIdentifier] = itemSession.session;\n\t\t}\n\t\treturn acc;\n\t}, {});\n}\n\nexport function buildActivitySessionPatchFromTestAttemptSession(\n\ttestAttemptSession: TestAttemptSession,\n): ActivitySessionPatchPayload {\n\treturn {\n\t\tactivitySession: {\n\t\t\tid: testAttemptSession.testAttemptSessionIdentifier,\n\t\t\tstartedAt: testAttemptSession.startedAt,\n\t\t\tupdatedAt: testAttemptSession.updatedAt,\n\t\t\tcompletedAt: testAttemptSession.completedAt,\n\t\t\tcurrentItemIndex: testAttemptSession.navigationState.currentItemIndex,\n\t\t\tcurrentSectionIdentifier:\n\t\t\t\ttestAttemptSession.navigationState.currentSectionIdentifier,\n\t\t\tvisitedItemIdentifiers: [\n\t\t\t\t...testAttemptSession.navigationState.visitedItemIdentifiers,\n\t\t\t],\n\t\t\titemSessions: toItemSessionsRecord(testAttemptSession),\n\t\t},\n\t};\n}\n\nexport function buildActivitySessionItemUpdate(\n\ttestAttemptSession: TestAttemptSession,\n\titemIdentifier: string,\n): ActivitySessionPatchPayload {\n\tconst itemSession = testAttemptSession.itemSessions[itemIdentifier];\n\treturn {\n\t\tactivitySession: {\n\t\t\tid: testAttemptSession.testAttemptSessionIdentifier,\n\t\t\tcurrentItemIndex: testAttemptSession.navigationState.currentItemIndex,\n\t\t\tcurrentSectionIdentifier:\n\t\t\t\ttestAttemptSession.navigationState.currentSectionIdentifier,\n\t\t\tvisitedItemIdentifiers: [\n\t\t\t\t...testAttemptSession.navigationState.visitedItemIdentifiers,\n\t\t\t],\n\t\t\titemSessions: itemSession?.session\n\t\t\t\t? { [itemIdentifier]: itemSession.session }\n\t\t\t\t: {},\n\t\t},\n\t};\n}\n"]}
@@ -6223,6 +6223,39 @@ var segmentSentences = (text2, options) => {
6223
6223
  };
6224
6224
 
6225
6225
  // dist/services/tts/browser-provider.js
6226
+ var normalizeLanguageCode = (value) => String(value || "").trim().toLowerCase();
6227
+ var browserLanguage = () => {
6228
+ const navigatorLanguage = typeof navigator !== "undefined" ? navigator.language || navigator.languages?.[0] : "";
6229
+ return normalizeLanguageCode(navigatorLanguage || "en-US");
6230
+ };
6231
+ var findBrowserVoice = (voices, preferredName) => {
6232
+ if (preferredName) {
6233
+ const explicit = voices.find((voice) => voice.name === preferredName);
6234
+ if (explicit)
6235
+ return explicit;
6236
+ }
6237
+ const language = browserLanguage();
6238
+ const languagePrefix = language.split("-")[0] || "en";
6239
+ const matchesLanguage = (voice) => {
6240
+ const voiceLanguage = normalizeLanguageCode(voice.lang);
6241
+ return voiceLanguage === language || voiceLanguage.startsWith(`${languagePrefix}-`);
6242
+ };
6243
+ const ranked = [
6244
+ (voice) => voice.localService && matchesLanguage(voice),
6245
+ (voice) => voice.default && matchesLanguage(voice),
6246
+ (voice) => matchesLanguage(voice),
6247
+ (voice) => voice.localService,
6248
+ (voice) => voice.default
6249
+ ];
6250
+ for (const predicate of ranked) {
6251
+ const voice = voices.find(predicate);
6252
+ if (voice)
6253
+ return voice;
6254
+ }
6255
+ return voices[0] || null;
6256
+ };
6257
+ var shouldAssignBrowserVoice = (voice) => !voice.default;
6258
+
6226
6259
  class BrowserTTSProvider {
6227
6260
  providerId = "browser";
6228
6261
  providerName = "Browser Speech Synthesis";
@@ -6377,24 +6410,28 @@ class BrowserTTSProviderImpl {
6377
6410
  resolve(false);
6378
6411
  return;
6379
6412
  }
6380
- this.utterance = new SpeechSynthesisUtterance(chunkText);
6381
- if (this.config?.voice) {
6382
- const voices = speechSynthesis.getVoices();
6383
- const voice = voices.find((v) => v.name === this.config.voice);
6384
- if (voice)
6385
- this.utterance.voice = voice;
6386
- }
6413
+ const utterance = new SpeechSynthesisUtterance(chunkText);
6414
+ this.utterance = utterance;
6415
+ const voice = findBrowserVoice(speechSynthesis.getVoices(), this.config?.voice);
6416
+ if (voice && shouldAssignBrowserVoice(voice))
6417
+ utterance.voice = voice;
6387
6418
  if (this.config?.rate)
6388
- this.utterance.rate = this.config.rate;
6419
+ utterance.rate = this.config.rate;
6389
6420
  if (this.config?.pitch)
6390
- this.utterance.pitch = this.config.pitch;
6391
- this.utterance.onstart = () => {
6421
+ utterance.pitch = this.config.pitch;
6422
+ const clearOwnedUtterance = () => {
6423
+ if (this.utterance === utterance) {
6424
+ this.utterance = null;
6425
+ }
6426
+ };
6427
+ utterance.onstart = () => {
6392
6428
  if (runId !== this.speakRunId)
6393
6429
  return;
6394
6430
  this._isPlaying = true;
6395
6431
  this._isPaused = false;
6396
6432
  };
6397
- this.utterance.onend = () => {
6433
+ utterance.onend = () => {
6434
+ clearOwnedUtterance();
6398
6435
  if (runId !== this.speakRunId) {
6399
6436
  resolve(false);
6400
6437
  return;
@@ -6403,7 +6440,8 @@ class BrowserTTSProviderImpl {
6403
6440
  this._isPaused = false;
6404
6441
  resolve(true);
6405
6442
  };
6406
- this.utterance.onerror = (event2) => {
6443
+ utterance.onerror = (event2) => {
6444
+ clearOwnedUtterance();
6407
6445
  if (runId !== this.speakRunId) {
6408
6446
  resolve(false);
6409
6447
  return;
@@ -6416,17 +6454,17 @@ class BrowserTTSProviderImpl {
6416
6454
  }
6417
6455
  reject(new Error(`Speech synthesis error: ${event2.error}`));
6418
6456
  };
6419
- this.utterance.onpause = () => {
6457
+ utterance.onpause = () => {
6420
6458
  if (runId !== this.speakRunId)
6421
6459
  return;
6422
6460
  this._isPaused = true;
6423
6461
  };
6424
- this.utterance.onresume = () => {
6462
+ utterance.onresume = () => {
6425
6463
  if (runId !== this.speakRunId)
6426
6464
  return;
6427
6465
  this._isPaused = false;
6428
6466
  };
6429
- this.utterance.onboundary = (event2) => {
6467
+ utterance.onboundary = (event2) => {
6430
6468
  if (runId !== this.speakRunId)
6431
6469
  return;
6432
6470
  console.log("[BrowserProvider] Boundary event:", event2.name, "charIndex:", event2.charIndex, "charLength:", event2.charLength);
@@ -6444,7 +6482,7 @@ class BrowserTTSProviderImpl {
6444
6482
  console.log("[BrowserProvider] Calling onWordBoundary with word:", word, "at position:", absoluteBoundaryStart);
6445
6483
  this.onWordBoundary(word, absoluteBoundaryStart, wordLength);
6446
6484
  };
6447
- speechSynthesis.speak(this.utterance);
6485
+ speechSynthesis.speak(utterance);
6448
6486
  });
6449
6487
  }
6450
6488
  pause() {
@@ -6459,9 +6497,10 @@ class BrowserTTSProviderImpl {
6459
6497
  }
6460
6498
  stop() {
6461
6499
  this.speakRunId += 1;
6462
- if (this._isPlaying) {
6500
+ if (this._isPlaying || this.utterance) {
6463
6501
  speechSynthesis.cancel();
6464
6502
  }
6503
+ this.utterance = null;
6465
6504
  this._isPlaying = false;
6466
6505
  this._isPaused = false;
6467
6506
  }
@@ -39,7 +39,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
39
39
 
40
40
  // ../../node_modules/.bun/speech-rule-engine@5.0.0-rc.1/node_modules/speech-rule-engine/lib/sre.js
41
41
  var require_sre = __commonJS((exports, module) => {
42
- var __dirname = "/home/runner/work/pie-players/pie-players/node_modules/.bun/speech-rule-engine@5.0.0-rc.1/node_modules/speech-rule-engine/lib";
42
+ var __dirname = "/Users/eelco.hillenius/dev/prj/pie/pie-players/node_modules/.bun/speech-rule-engine@5.0.0-rc.1/node_modules/speech-rule-engine/lib";
43
43
  (function(t, e) {
44
44
  typeof exports == "object" && typeof module == "object" ? module.exports = e() : typeof define == "function" && define.amd ? define([], e) : typeof exports == "object" ? exports.SRE = e() : t.SRE = e();
45
45
  })(exports, () => (() => {
@@ -18080,6 +18080,39 @@ var segmentSentences = (text2, options) => {
18080
18080
  };
18081
18081
 
18082
18082
  // dist/services/tts/browser-provider.js
18083
+ var normalizeLanguageCode = (value) => String(value || "").trim().toLowerCase();
18084
+ var browserLanguage = () => {
18085
+ const navigatorLanguage = typeof navigator !== "undefined" ? navigator.language || navigator.languages?.[0] : "";
18086
+ return normalizeLanguageCode(navigatorLanguage || "en-US");
18087
+ };
18088
+ var findBrowserVoice = (voices, preferredName) => {
18089
+ if (preferredName) {
18090
+ const explicit = voices.find((voice) => voice.name === preferredName);
18091
+ if (explicit)
18092
+ return explicit;
18093
+ }
18094
+ const language = browserLanguage();
18095
+ const languagePrefix = language.split("-")[0] || "en";
18096
+ const matchesLanguage = (voice) => {
18097
+ const voiceLanguage = normalizeLanguageCode(voice.lang);
18098
+ return voiceLanguage === language || voiceLanguage.startsWith(`${languagePrefix}-`);
18099
+ };
18100
+ const ranked = [
18101
+ (voice) => voice.localService && matchesLanguage(voice),
18102
+ (voice) => voice.default && matchesLanguage(voice),
18103
+ (voice) => matchesLanguage(voice),
18104
+ (voice) => voice.localService,
18105
+ (voice) => voice.default
18106
+ ];
18107
+ for (const predicate of ranked) {
18108
+ const voice = voices.find(predicate);
18109
+ if (voice)
18110
+ return voice;
18111
+ }
18112
+ return voices[0] || null;
18113
+ };
18114
+ var shouldAssignBrowserVoice = (voice) => !voice.default;
18115
+
18083
18116
  class BrowserTTSProvider {
18084
18117
  providerId = "browser";
18085
18118
  providerName = "Browser Speech Synthesis";
@@ -18234,24 +18267,28 @@ class BrowserTTSProviderImpl {
18234
18267
  resolve(false);
18235
18268
  return;
18236
18269
  }
18237
- this.utterance = new SpeechSynthesisUtterance(chunkText);
18238
- if (this.config?.voice) {
18239
- const voices = speechSynthesis.getVoices();
18240
- const voice = voices.find((v) => v.name === this.config.voice);
18241
- if (voice)
18242
- this.utterance.voice = voice;
18243
- }
18270
+ const utterance = new SpeechSynthesisUtterance(chunkText);
18271
+ this.utterance = utterance;
18272
+ const voice = findBrowserVoice(speechSynthesis.getVoices(), this.config?.voice);
18273
+ if (voice && shouldAssignBrowserVoice(voice))
18274
+ utterance.voice = voice;
18244
18275
  if (this.config?.rate)
18245
- this.utterance.rate = this.config.rate;
18276
+ utterance.rate = this.config.rate;
18246
18277
  if (this.config?.pitch)
18247
- this.utterance.pitch = this.config.pitch;
18248
- this.utterance.onstart = () => {
18278
+ utterance.pitch = this.config.pitch;
18279
+ const clearOwnedUtterance = () => {
18280
+ if (this.utterance === utterance) {
18281
+ this.utterance = null;
18282
+ }
18283
+ };
18284
+ utterance.onstart = () => {
18249
18285
  if (runId !== this.speakRunId)
18250
18286
  return;
18251
18287
  this._isPlaying = true;
18252
18288
  this._isPaused = false;
18253
18289
  };
18254
- this.utterance.onend = () => {
18290
+ utterance.onend = () => {
18291
+ clearOwnedUtterance();
18255
18292
  if (runId !== this.speakRunId) {
18256
18293
  resolve(false);
18257
18294
  return;
@@ -18260,7 +18297,8 @@ class BrowserTTSProviderImpl {
18260
18297
  this._isPaused = false;
18261
18298
  resolve(true);
18262
18299
  };
18263
- this.utterance.onerror = (event2) => {
18300
+ utterance.onerror = (event2) => {
18301
+ clearOwnedUtterance();
18264
18302
  if (runId !== this.speakRunId) {
18265
18303
  resolve(false);
18266
18304
  return;
@@ -18273,17 +18311,17 @@ class BrowserTTSProviderImpl {
18273
18311
  }
18274
18312
  reject(new Error(`Speech synthesis error: ${event2.error}`));
18275
18313
  };
18276
- this.utterance.onpause = () => {
18314
+ utterance.onpause = () => {
18277
18315
  if (runId !== this.speakRunId)
18278
18316
  return;
18279
18317
  this._isPaused = true;
18280
18318
  };
18281
- this.utterance.onresume = () => {
18319
+ utterance.onresume = () => {
18282
18320
  if (runId !== this.speakRunId)
18283
18321
  return;
18284
18322
  this._isPaused = false;
18285
18323
  };
18286
- this.utterance.onboundary = (event2) => {
18324
+ utterance.onboundary = (event2) => {
18287
18325
  if (runId !== this.speakRunId)
18288
18326
  return;
18289
18327
  console.log("[BrowserProvider] Boundary event:", event2.name, "charIndex:", event2.charIndex, "charLength:", event2.charLength);
@@ -18301,7 +18339,7 @@ class BrowserTTSProviderImpl {
18301
18339
  console.log("[BrowserProvider] Calling onWordBoundary with word:", word, "at position:", absoluteBoundaryStart);
18302
18340
  this.onWordBoundary(word, absoluteBoundaryStart, wordLength);
18303
18341
  };
18304
- speechSynthesis.speak(this.utterance);
18342
+ speechSynthesis.speak(utterance);
18305
18343
  });
18306
18344
  }
18307
18345
  pause() {
@@ -18316,9 +18354,10 @@ class BrowserTTSProviderImpl {
18316
18354
  }
18317
18355
  stop() {
18318
18356
  this.speakRunId += 1;
18319
- if (this._isPlaying) {
18357
+ if (this._isPlaying || this.utterance) {
18320
18358
  speechSynthesis.cancel();
18321
18359
  }
18360
+ this.utterance = null;
18322
18361
  this._isPlaying = false;
18323
18362
  this._isPaused = false;
18324
18363
  }
@@ -21831,7 +21870,10 @@ var resolvePlainBoundaryOffset = (alignment, position, safeLength) => {
21831
21870
  const token = findTokenAtOffset(alignment.spokenTokens, position);
21832
21871
  if (!token)
21833
21872
  return null;
21834
- return { start: position, length: Math.min(safeLength, token.end - position) };
21873
+ return {
21874
+ start: position,
21875
+ length: Math.min(safeLength, token.end - position)
21876
+ };
21835
21877
  };
21836
21878
  var resolveSpokenBoundaryOffset = (alignment, position, length = 1, boundaryWord) => {
21837
21879
  if (alignment.boundaryOffsetMode === "unsupported" || !Number.isFinite(position)) {
@@ -22861,7 +22903,11 @@ var collectMathAware = (root, options) => {
22861
22903
  const flushTextChunk = () => {
22862
22904
  const text2 = normalizeTextForSpeech(acc.chars.slice(textChunkStart, acc.chars.length).join(""));
22863
22905
  if (text2) {
22864
- chunks.push({ type: "text", text: text2, sourceElement: textChunkSourceElement });
22906
+ chunks.push({
22907
+ type: "text",
22908
+ text: text2,
22909
+ sourceElement: textChunkSourceElement
22910
+ });
22865
22911
  }
22866
22912
  textChunkStart = acc.chars.length;
22867
22913
  textChunkSourceElement = undefined;
@@ -26896,27 +26942,36 @@ class ToolkitCoordinator {
26896
26942
  return;
26897
26943
  await new Promise((resolve) => {
26898
26944
  let settled2 = false;
26945
+ const canUseEventTarget = typeof synth.addEventListener === "function" && typeof synth.removeEventListener === "function";
26946
+ const canUseHandler = !canUseEventTarget && "onvoiceschanged" in synth;
26947
+ const previousHandler = canUseHandler ? synth.onvoiceschanged : null;
26948
+ let assignedHandler = false;
26899
26949
  const finish = () => {
26900
26950
  if (settled2)
26901
26951
  return;
26902
26952
  settled2 = true;
26903
- resolve();
26904
- };
26905
- const timeoutId = window.setTimeout(() => {
26906
- synth.removeEventListener("voiceschanged", onVoicesChanged);
26907
- finish();
26908
- }, timeoutMs);
26909
- const onVoicesChanged = () => {
26910
26953
  window.clearTimeout(timeoutId);
26911
- if (typeof synth.removeEventListener === "function" && typeof synth.addEventListener === "function") {
26954
+ if (canUseEventTarget) {
26912
26955
  synth.removeEventListener("voiceschanged", onVoicesChanged);
26956
+ } else if (assignedHandler && synth.onvoiceschanged === onVoicesChanged) {
26957
+ synth.onvoiceschanged = previousHandler;
26958
+ }
26959
+ resolve();
26960
+ };
26961
+ const onVoicesChanged = (event2) => {
26962
+ if (!canUseEventTarget && typeof previousHandler === "function" && event2) {
26963
+ previousHandler.call(synth, event2);
26913
26964
  }
26914
26965
  finish();
26915
26966
  };
26916
- if (typeof synth.addEventListener === "function" && typeof synth.removeEventListener === "function") {
26967
+ const timeoutId = window.setTimeout(finish, timeoutMs);
26968
+ if (canUseEventTarget) {
26917
26969
  synth.addEventListener("voiceschanged", onVoicesChanged, {
26918
26970
  once: true
26919
26971
  });
26972
+ } else if (canUseHandler) {
26973
+ synth.onvoiceschanged = onVoicesChanged;
26974
+ assignedHandler = true;
26920
26975
  }
26921
26976
  });
26922
26977
  const voicesAfterWait = synth.getVoices();
@@ -10415,6 +10415,38 @@ var segmentSentences = (text2, options) => {
10415
10415
  }
10416
10416
  return parsed.length > 0 ? parsed : [{ text: text2, offset: 0 }];
10417
10417
  };
10418
+ var normalizeLanguageCode = (value) => String(value || "").trim().toLowerCase();
10419
+ var browserLanguage = () => {
10420
+ const navigatorLanguage = typeof navigator !== "undefined" ? navigator.language || navigator.languages?.[0] : "";
10421
+ return normalizeLanguageCode(navigatorLanguage || "en-US");
10422
+ };
10423
+ var findBrowserVoice = (voices, preferredName) => {
10424
+ if (preferredName) {
10425
+ const explicit = voices.find((voice) => voice.name === preferredName);
10426
+ if (explicit)
10427
+ return explicit;
10428
+ }
10429
+ const language = browserLanguage();
10430
+ const languagePrefix = language.split("-")[0] || "en";
10431
+ const matchesLanguage = (voice) => {
10432
+ const voiceLanguage = normalizeLanguageCode(voice.lang);
10433
+ return voiceLanguage === language || voiceLanguage.startsWith(`${languagePrefix}-`);
10434
+ };
10435
+ const ranked = [
10436
+ (voice) => voice.localService && matchesLanguage(voice),
10437
+ (voice) => voice.default && matchesLanguage(voice),
10438
+ (voice) => matchesLanguage(voice),
10439
+ (voice) => voice.localService,
10440
+ (voice) => voice.default
10441
+ ];
10442
+ for (const predicate of ranked) {
10443
+ const voice = voices.find(predicate);
10444
+ if (voice)
10445
+ return voice;
10446
+ }
10447
+ return voices[0] || null;
10448
+ };
10449
+ var shouldAssignBrowserVoice = (voice) => !voice.default;
10418
10450
 
10419
10451
  class BrowserTTSProvider {
10420
10452
  providerId = "browser";
@@ -10570,24 +10602,28 @@ class BrowserTTSProviderImpl {
10570
10602
  resolve(false);
10571
10603
  return;
10572
10604
  }
10573
- this.utterance = new SpeechSynthesisUtterance(chunkText);
10574
- if (this.config?.voice) {
10575
- const voices = speechSynthesis.getVoices();
10576
- const voice = voices.find((v) => v.name === this.config.voice);
10577
- if (voice)
10578
- this.utterance.voice = voice;
10579
- }
10605
+ const utterance = new SpeechSynthesisUtterance(chunkText);
10606
+ this.utterance = utterance;
10607
+ const voice = findBrowserVoice(speechSynthesis.getVoices(), this.config?.voice);
10608
+ if (voice && shouldAssignBrowserVoice(voice))
10609
+ utterance.voice = voice;
10580
10610
  if (this.config?.rate)
10581
- this.utterance.rate = this.config.rate;
10611
+ utterance.rate = this.config.rate;
10582
10612
  if (this.config?.pitch)
10583
- this.utterance.pitch = this.config.pitch;
10584
- this.utterance.onstart = () => {
10613
+ utterance.pitch = this.config.pitch;
10614
+ const clearOwnedUtterance = () => {
10615
+ if (this.utterance === utterance) {
10616
+ this.utterance = null;
10617
+ }
10618
+ };
10619
+ utterance.onstart = () => {
10585
10620
  if (runId !== this.speakRunId)
10586
10621
  return;
10587
10622
  this._isPlaying = true;
10588
10623
  this._isPaused = false;
10589
10624
  };
10590
- this.utterance.onend = () => {
10625
+ utterance.onend = () => {
10626
+ clearOwnedUtterance();
10591
10627
  if (runId !== this.speakRunId) {
10592
10628
  resolve(false);
10593
10629
  return;
@@ -10596,7 +10632,8 @@ class BrowserTTSProviderImpl {
10596
10632
  this._isPaused = false;
10597
10633
  resolve(true);
10598
10634
  };
10599
- this.utterance.onerror = (event2) => {
10635
+ utterance.onerror = (event2) => {
10636
+ clearOwnedUtterance();
10600
10637
  if (runId !== this.speakRunId) {
10601
10638
  resolve(false);
10602
10639
  return;
@@ -10609,17 +10646,17 @@ class BrowserTTSProviderImpl {
10609
10646
  }
10610
10647
  reject(new Error(`Speech synthesis error: ${event2.error}`));
10611
10648
  };
10612
- this.utterance.onpause = () => {
10649
+ utterance.onpause = () => {
10613
10650
  if (runId !== this.speakRunId)
10614
10651
  return;
10615
10652
  this._isPaused = true;
10616
10653
  };
10617
- this.utterance.onresume = () => {
10654
+ utterance.onresume = () => {
10618
10655
  if (runId !== this.speakRunId)
10619
10656
  return;
10620
10657
  this._isPaused = false;
10621
10658
  };
10622
- this.utterance.onboundary = (event2) => {
10659
+ utterance.onboundary = (event2) => {
10623
10660
  if (runId !== this.speakRunId)
10624
10661
  return;
10625
10662
  console.log("[BrowserProvider] Boundary event:", event2.name, "charIndex:", event2.charIndex, "charLength:", event2.charLength);
@@ -10637,7 +10674,7 @@ class BrowserTTSProviderImpl {
10637
10674
  console.log("[BrowserProvider] Calling onWordBoundary with word:", word, "at position:", absoluteBoundaryStart);
10638
10675
  this.onWordBoundary(word, absoluteBoundaryStart, wordLength);
10639
10676
  };
10640
- speechSynthesis.speak(this.utterance);
10677
+ speechSynthesis.speak(utterance);
10641
10678
  });
10642
10679
  }
10643
10680
  pause() {
@@ -10652,9 +10689,10 @@ class BrowserTTSProviderImpl {
10652
10689
  }
10653
10690
  stop() {
10654
10691
  this.speakRunId += 1;
10655
- if (this._isPlaying) {
10692
+ if (this._isPlaying || this.utterance) {
10656
10693
  speechSynthesis.cancel();
10657
10694
  }
10695
+ this.utterance = null;
10658
10696
  this._isPlaying = false;
10659
10697
  this._isPaused = false;
10660
10698
  }
package/dist/index.d.ts CHANGED
@@ -43,7 +43,7 @@ export { ToolkitCoordinator } from "./services/ToolkitCoordinator.js";
43
43
  export type { CanonicalToolsConfig, ToolPlacementConfig, ToolPlacementLevel, ToolPolicyConfig, ToolProvidersConfig, } from "./services/tools-config-normalizer.js";
44
44
  export type { FrameworkErrorKind, FrameworkErrorModel, FrameworkErrorSeverity, } from "./services/framework-error.js";
45
45
  export { formatFrameworkErrorForConsole, frameworkErrorFromToolConfigDiagnostics, frameworkErrorFromUnknown, toFrameworkErrorModel, } from "./services/framework-error.js";
46
- export type { FrameworkErrorListener, } from "./services/framework-error-bus.js";
46
+ export type { FrameworkErrorListener } from "./services/framework-error-bus.js";
47
47
  export type { ToolConfigDiagnostic, ToolConfigDiagnosticSeverity, ToolConfigStrictness, ToolConfigValidationOptions, ToolConfigValidationResult, } from "./services/tool-config-validation.js";
48
48
  export type { ToolbarButtonItem, ToolbarItem, ToolbarItemBase, ToolbarLinkItem, } from "./services/toolbar-items.js";
49
49
  export { isExternalIconUrl, isInlineSvgIcon, isToolbarLinkItem, isValidToolbarItemShape, } from "./services/toolbar-items.js";