@pie-players/pie-assessment-toolkit 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +520 -0
- package/dist/attempt/TestSession.d.ts +90 -0
- package/dist/attempt/TestSession.d.ts.map +1 -0
- package/dist/attempt/TestSession.js +174 -0
- package/dist/attempt/TestSession.js.map +1 -0
- package/dist/core/TypedEventBus.d.ts +48 -0
- package/dist/core/TypedEventBus.d.ts.map +1 -0
- package/dist/core/TypedEventBus.js +63 -0
- package/dist/core/TypedEventBus.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/item-loader.d.ts +45 -0
- package/dist/item-loader.d.ts.map +1 -0
- package/dist/item-loader.js +62 -0
- package/dist/item-loader.js.map +1 -0
- package/dist/player/AssessmentPlayer.d.ts +388 -0
- package/dist/player/AssessmentPlayer.d.ts.map +1 -0
- package/dist/player/AssessmentPlayer.js +965 -0
- package/dist/player/AssessmentPlayer.js.map +1 -0
- package/dist/player/index.d.ts +17 -0
- package/dist/player/index.d.ts.map +1 -0
- package/dist/player/index.js +15 -0
- package/dist/player/index.js.map +1 -0
- package/dist/player/navigation-types.d.ts +63 -0
- package/dist/player/navigation-types.d.ts.map +1 -0
- package/dist/player/navigation-types.js +7 -0
- package/dist/player/navigation-types.js.map +1 -0
- package/dist/player/qti-navigation.d.ts +29 -0
- package/dist/player/qti-navigation.d.ts.map +1 -0
- package/dist/player/qti-navigation.js +160 -0
- package/dist/player/qti-navigation.js.map +1 -0
- package/dist/reference-layout/index.d.ts +19 -0
- package/dist/reference-layout/index.d.ts.map +1 -0
- package/dist/reference-layout/index.js +20 -0
- package/dist/reference-layout/index.js.map +1 -0
- package/dist/services/AccessibilityCatalogResolver.d.ts +141 -0
- package/dist/services/AccessibilityCatalogResolver.d.ts.map +1 -0
- package/dist/services/AccessibilityCatalogResolver.js +249 -0
- package/dist/services/AccessibilityCatalogResolver.js.map +1 -0
- package/dist/services/AssessmentAuthoringService.d.ts +60 -0
- package/dist/services/AssessmentAuthoringService.d.ts.map +1 -0
- package/dist/services/AssessmentAuthoringService.js +183 -0
- package/dist/services/AssessmentAuthoringService.js.map +1 -0
- package/dist/services/ContextVariableStore.d.ts +114 -0
- package/dist/services/ContextVariableStore.d.ts.map +1 -0
- package/dist/services/ContextVariableStore.js +207 -0
- package/dist/services/ContextVariableStore.js.map +1 -0
- package/dist/services/ElementToolStateStore.d.ts +125 -0
- package/dist/services/ElementToolStateStore.d.ts.map +1 -0
- package/dist/services/ElementToolStateStore.js +200 -0
- package/dist/services/ElementToolStateStore.js.map +1 -0
- package/dist/services/HighlightCoordinator.d.ts +179 -0
- package/dist/services/HighlightCoordinator.d.ts.map +1 -0
- package/dist/services/HighlightCoordinator.js +446 -0
- package/dist/services/HighlightCoordinator.js.map +1 -0
- package/dist/services/I18nService.d.ts +121 -0
- package/dist/services/I18nService.d.ts.map +1 -0
- package/dist/services/I18nService.js +276 -0
- package/dist/services/I18nService.js.map +1 -0
- package/dist/services/PNPMapper.d.ts +71 -0
- package/dist/services/PNPMapper.d.ts.map +1 -0
- package/dist/services/PNPMapper.js +98 -0
- package/dist/services/PNPMapper.js.map +1 -0
- package/dist/services/PNPToolResolver.d.ts +127 -0
- package/dist/services/PNPToolResolver.d.ts.map +1 -0
- package/dist/services/PNPToolResolver.js +222 -0
- package/dist/services/PNPToolResolver.js.map +1 -0
- package/dist/services/SSMLExtractor.d.ts +61 -0
- package/dist/services/SSMLExtractor.d.ts.map +1 -0
- package/dist/services/SSMLExtractor.js +181 -0
- package/dist/services/SSMLExtractor.js.map +1 -0
- package/dist/services/TTSService.d.ts +162 -0
- package/dist/services/TTSService.d.ts.map +1 -0
- package/dist/services/TTSService.js +465 -0
- package/dist/services/TTSService.js.map +1 -0
- package/dist/services/ThemeProvider.d.ts +95 -0
- package/dist/services/ThemeProvider.d.ts.map +1 -0
- package/dist/services/ThemeProvider.js +299 -0
- package/dist/services/ThemeProvider.js.map +1 -0
- package/dist/services/ToolConfigResolver.d.ts +162 -0
- package/dist/services/ToolConfigResolver.d.ts.map +1 -0
- package/dist/services/ToolConfigResolver.js +230 -0
- package/dist/services/ToolConfigResolver.js.map +1 -0
- package/dist/services/ToolCoordinator.d.ts +137 -0
- package/dist/services/ToolCoordinator.d.ts.map +1 -0
- package/dist/services/ToolCoordinator.js +318 -0
- package/dist/services/ToolCoordinator.js.map +1 -0
- package/dist/services/ToolkitCoordinator.d.ts +188 -0
- package/dist/services/ToolkitCoordinator.d.ts.map +1 -0
- package/dist/services/ToolkitCoordinator.js +252 -0
- package/dist/services/ToolkitCoordinator.js.map +1 -0
- package/dist/services/interfaces.d.ts +391 -0
- package/dist/services/interfaces.d.ts.map +1 -0
- package/dist/services/interfaces.js +12 -0
- package/dist/services/interfaces.js.map +1 -0
- package/dist/services/tool-providers/DesmosToolProvider.d.ts +103 -0
- package/dist/services/tool-providers/DesmosToolProvider.d.ts.map +1 -0
- package/dist/services/tool-providers/DesmosToolProvider.js +127 -0
- package/dist/services/tool-providers/DesmosToolProvider.js.map +1 -0
- package/dist/services/tool-providers/IToolProvider.d.ts +137 -0
- package/dist/services/tool-providers/IToolProvider.d.ts.map +1 -0
- package/dist/services/tool-providers/IToolProvider.js +13 -0
- package/dist/services/tool-providers/IToolProvider.js.map +1 -0
- package/dist/services/tool-providers/TIToolProvider.d.ts +107 -0
- package/dist/services/tool-providers/TIToolProvider.d.ts.map +1 -0
- package/dist/services/tool-providers/TIToolProvider.js +123 -0
- package/dist/services/tool-providers/TIToolProvider.js.map +1 -0
- package/dist/services/tool-providers/TTSToolProvider.d.ts +143 -0
- package/dist/services/tool-providers/TTSToolProvider.d.ts.map +1 -0
- package/dist/services/tool-providers/TTSToolProvider.js +168 -0
- package/dist/services/tool-providers/TTSToolProvider.js.map +1 -0
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts +165 -0
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts.map +1 -0
- package/dist/services/tool-providers/ToolProviderRegistry.js +240 -0
- package/dist/services/tool-providers/ToolProviderRegistry.js.map +1 -0
- package/dist/services/tool-providers/index.d.ts +18 -0
- package/dist/services/tool-providers/index.d.ts.map +1 -0
- package/dist/services/tool-providers/index.js +15 -0
- package/dist/services/tool-providers/index.js.map +1 -0
- package/dist/services/tts/browser-provider.d.ts +24 -0
- package/dist/services/tts/browser-provider.d.ts.map +1 -0
- package/dist/services/tts/browser-provider.js +177 -0
- package/dist/services/tts/browser-provider.js.map +1 -0
- package/dist/services/tts/provider-interface.d.ts +131 -0
- package/dist/services/tts/provider-interface.d.ts.map +1 -0
- package/dist/services/tts/provider-interface.js +10 -0
- package/dist/services/tts/provider-interface.js.map +1 -0
- package/dist/tools/calculators/desmos-provider.d.ts +54 -0
- package/dist/tools/calculators/desmos-provider.d.ts.map +1 -0
- package/dist/tools/calculators/desmos-provider.js +384 -0
- package/dist/tools/calculators/desmos-provider.js.map +1 -0
- package/dist/tools/calculators/mathjs-provider.d.ts +50 -0
- package/dist/tools/calculators/mathjs-provider.d.ts.map +1 -0
- package/dist/tools/calculators/mathjs-provider.js +660 -0
- package/dist/tools/calculators/mathjs-provider.js.map +1 -0
- package/dist/tools/calculators/ti-provider.d.ts +122 -0
- package/dist/tools/calculators/ti-provider.d.ts.map +1 -0
- package/dist/tools/calculators/ti-provider.js +587 -0
- package/dist/tools/calculators/ti-provider.js.map +1 -0
- package/dist/tools/client.d.ts +17 -0
- package/dist/tools/client.d.ts.map +1 -0
- package/dist/tools/client.js +26 -0
- package/dist/tools/client.js.map +1 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +10 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/library-loader.d.ts +63 -0
- package/dist/tools/library-loader.d.ts.map +1 -0
- package/dist/tools/library-loader.js +292 -0
- package/dist/tools/library-loader.js.map +1 -0
- package/dist/tools/response-discovery.d.ts +72 -0
- package/dist/tools/response-discovery.d.ts.map +1 -0
- package/dist/tools/response-discovery.js +183 -0
- package/dist/tools/response-discovery.js.map +1 -0
- package/dist/tools/tool-coordinator.d.ts +76 -0
- package/dist/tools/tool-coordinator.d.ts.map +1 -0
- package/dist/tools/tool-coordinator.js +197 -0
- package/dist/tools/tool-coordinator.js.map +1 -0
- package/dist/tools/types.d.ts +427 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +13 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/variant-resolver.d.ts +48 -0
- package/dist/tools/variant-resolver.d.ts.map +1 -0
- package/dist/tools/variant-resolver.js +214 -0
- package/dist/tools/variant-resolver.js.map +1 -0
- package/dist/types/events.d.ts +157 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +11 -0
- package/dist/types/events.js.map +1 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +11 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +71 -0
- package/src/README.md +626 -0
- package/src/components/QuestionToolBar.svelte +406 -0
- package/src/player/AssessmentLayout.svelte +68 -0
- package/src/player/README.md +154 -0
- package/src/reference-layout/README.md +135 -0
- package/src/reference-layout/ReferenceLayout.svelte +209 -0
- package/src/reference-layout/components/AssessmentContent.svelte +259 -0
- package/src/reference-layout/components/AssessmentFooter.svelte +89 -0
- package/src/reference-layout/components/AssessmentHeader.svelte +250 -0
- package/src/reference-layout/components/AssessmentNavigation.svelte +134 -0
- package/src/reference-layout/components/AssessmentToolsBar.svelte +176 -0
- package/src/reference-layout/components/ItemPanel.svelte +200 -0
- package/src/reference-layout/components/NotesPanel.svelte +179 -0
- package/src/reference-layout/components/PassagePanel.svelte +76 -0
- package/src/tools/README.md +616 -0
- package/src/tools/calculators/README.md +395 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PNP Tool Resolver
|
|
3
|
+
*
|
|
4
|
+
* Resolves tool availability from QTI 3.0 Personal Needs Profile (PNP) and
|
|
5
|
+
* assessment settings. Implements precedence hierarchy for tool configuration.
|
|
6
|
+
*
|
|
7
|
+
* Precedence (highest to lowest):
|
|
8
|
+
* 1. District block (absolute veto)
|
|
9
|
+
* 2. Test administration override
|
|
10
|
+
* 3. Item restriction (per-item block)
|
|
11
|
+
* 4. Item requirement (forces enable)
|
|
12
|
+
* 5. District requirement
|
|
13
|
+
* 6. PNP supports (student needs)
|
|
14
|
+
*
|
|
15
|
+
* Part of PIE Assessment Toolkit.
|
|
16
|
+
*/
|
|
17
|
+
import type { AssessmentEntity, AssessmentItemRef } from "@pie-players/pie-players-shared/types";
|
|
18
|
+
/**
|
|
19
|
+
* Resolved tool configuration with source tracking
|
|
20
|
+
*/
|
|
21
|
+
export interface ResolvedToolConfig {
|
|
22
|
+
id: string;
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
required?: boolean;
|
|
25
|
+
alwaysAvailable?: boolean;
|
|
26
|
+
settings?: any;
|
|
27
|
+
source: "district" | "item" | "pnp" | "settings";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolves tool availability from QTI 3.0 assessment structure
|
|
31
|
+
*/
|
|
32
|
+
export declare class PNPToolResolver {
|
|
33
|
+
/**
|
|
34
|
+
* Resolve all tools from QTI 3.0 assessment + optional item context
|
|
35
|
+
*
|
|
36
|
+
* @param assessment QTI 3.0 assessment with personalNeedsProfile
|
|
37
|
+
* @param currentItemRef Current item for item-specific requirements (optional)
|
|
38
|
+
* @returns Array of resolved tool configurations
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* const resolver = new PNPToolResolver();
|
|
42
|
+
* const tools = resolver.resolveTools(assessment, itemRef);
|
|
43
|
+
* tools.forEach(tool => {
|
|
44
|
+
* console.log(`${tool.id}: ${tool.enabled ? 'enabled' : 'disabled'}`);
|
|
45
|
+
* });
|
|
46
|
+
*/
|
|
47
|
+
resolveTools(assessment: AssessmentEntity, currentItemRef?: AssessmentItemRef): ResolvedToolConfig[];
|
|
48
|
+
/**
|
|
49
|
+
* Resolve availability of a single PNP support
|
|
50
|
+
*
|
|
51
|
+
* Applies precedence rules to determine if support should be enabled.
|
|
52
|
+
*
|
|
53
|
+
* @param supportId PNP support identifier
|
|
54
|
+
* @param context Resolution context with all configuration sources
|
|
55
|
+
* @returns Resolved config or null if blocked/disabled
|
|
56
|
+
*/
|
|
57
|
+
private resolveSupport;
|
|
58
|
+
/**
|
|
59
|
+
* Build resolved tool configuration
|
|
60
|
+
*
|
|
61
|
+
* Maps PNP support ID to PIE tool ID and merges tool-specific settings.
|
|
62
|
+
*
|
|
63
|
+
* @param supportId PNP support identifier
|
|
64
|
+
* @param context Resolution context for settings lookup
|
|
65
|
+
* @param flags Availability flags (required, alwaysAvailable, source)
|
|
66
|
+
* @returns Complete resolved tool configuration
|
|
67
|
+
*/
|
|
68
|
+
private buildToolConfig;
|
|
69
|
+
/**
|
|
70
|
+
* Check if a specific tool is enabled
|
|
71
|
+
*
|
|
72
|
+
* @param toolId PIE tool identifier (e.g., 'pie-tool-calculator')
|
|
73
|
+
* @param assessment Assessment with PNP
|
|
74
|
+
* @param itemRef Optional item context
|
|
75
|
+
* @returns true if tool is enabled
|
|
76
|
+
*/
|
|
77
|
+
isToolEnabled(toolId: string, assessment: AssessmentEntity, itemRef?: AssessmentItemRef): boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Check if a tool is required (cannot be disabled)
|
|
80
|
+
*
|
|
81
|
+
* @param toolId PIE tool identifier
|
|
82
|
+
* @param assessment Assessment with PNP
|
|
83
|
+
* @param itemRef Optional item context
|
|
84
|
+
* @returns true if tool is required or always available
|
|
85
|
+
*/
|
|
86
|
+
isToolRequired(toolId: string, assessment: AssessmentEntity, itemRef?: AssessmentItemRef): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Get list of auto-activate tools from PNP
|
|
89
|
+
*
|
|
90
|
+
* Returns PIE tool IDs that should be automatically opened/activated
|
|
91
|
+
* when the assessment starts, based on PNP activateAtInit field.
|
|
92
|
+
*
|
|
93
|
+
* @param assessment Assessment with PNP
|
|
94
|
+
* @returns Array of PIE tool IDs to auto-activate
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* const autoActivate = resolver.getAutoActivateTools(assessment);
|
|
98
|
+
* // Returns: ['pie-tool-text-to-speech', 'pie-tool-line-reader']
|
|
99
|
+
*/
|
|
100
|
+
getAutoActivateTools(assessment: AssessmentEntity): string[];
|
|
101
|
+
/**
|
|
102
|
+
* Get all enabled tools
|
|
103
|
+
*
|
|
104
|
+
* @param assessment Assessment with PNP
|
|
105
|
+
* @param itemRef Optional item context
|
|
106
|
+
* @returns Array of PIE tool IDs that are enabled
|
|
107
|
+
*/
|
|
108
|
+
getEnabledTools(assessment: AssessmentEntity, itemRef?: AssessmentItemRef): string[];
|
|
109
|
+
/**
|
|
110
|
+
* Get all required tools
|
|
111
|
+
*
|
|
112
|
+
* @param assessment Assessment with PNP
|
|
113
|
+
* @param itemRef Optional item context
|
|
114
|
+
* @returns Array of PIE tool IDs that are required
|
|
115
|
+
*/
|
|
116
|
+
getRequiredTools(assessment: AssessmentEntity, itemRef?: AssessmentItemRef): string[];
|
|
117
|
+
/**
|
|
118
|
+
* Get tool configuration settings
|
|
119
|
+
*
|
|
120
|
+
* @param toolId PIE tool identifier
|
|
121
|
+
* @param assessment Assessment with PNP
|
|
122
|
+
* @param itemRef Optional item context
|
|
123
|
+
* @returns Tool-specific settings or null if not found
|
|
124
|
+
*/
|
|
125
|
+
getToolSettings(toolId: string, assessment: AssessmentEntity, itemRef?: AssessmentItemRef): any | null;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=PNPToolResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PNPToolResolver.d.ts","sourceRoot":"","sources":["../../src/services/PNPToolResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EACX,gBAAgB,EAChB,iBAAiB,EAIjB,MAAM,uCAAuC,CAAC;AAG/C;;GAEG;AACH,MAAM,WAAW,kBAAkB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,KAAK,GAAG,UAAU,CAAC;CACjD;AAaD;;GAEG;AACH,qBAAa,eAAe;IAC3B;;;;;;;;;;;;;OAaG;IACH,YAAY,CACX,UAAU,EAAE,gBAAgB,EAC5B,cAAc,CAAC,EAAE,iBAAiB,GAChC,kBAAkB,EAAE;IAgCvB;;;;;;;;OAQG;IACH,OAAO,CAAC,cAAc;IAsDtB;;;;;;;;;OASG;IACH,OAAO,CAAC,eAAe;IA2BvB;;;;;;;OAOG;IACH,aAAa,CACZ,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,gBAAgB,EAC5B,OAAO,CAAC,EAAE,iBAAiB,GACzB,OAAO;IAKV;;;;;;;OAOG;IACH,cAAc,CACb,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,gBAAgB,EAC5B,OAAO,CAAC,EAAE,iBAAiB,GACzB,OAAO;IAMV;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,UAAU,EAAE,gBAAgB,GAAG,MAAM,EAAE;IAS5D;;;;;;OAMG;IACH,eAAe,CACd,UAAU,EAAE,gBAAgB,EAC5B,OAAO,CAAC,EAAE,iBAAiB,GACzB,MAAM,EAAE;IAKX;;;;;;OAMG;IACH,gBAAgB,CACf,UAAU,EAAE,gBAAgB,EAC5B,OAAO,CAAC,EAAE,iBAAiB,GACzB,MAAM,EAAE;IAOX;;;;;;;OAOG;IACH,eAAe,CACd,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,gBAAgB,EAC5B,OAAO,CAAC,EAAE,iBAAiB,GACzB,GAAG,GAAG,IAAI;CAKb"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PNP Tool Resolver
|
|
3
|
+
*
|
|
4
|
+
* Resolves tool availability from QTI 3.0 Personal Needs Profile (PNP) and
|
|
5
|
+
* assessment settings. Implements precedence hierarchy for tool configuration.
|
|
6
|
+
*
|
|
7
|
+
* Precedence (highest to lowest):
|
|
8
|
+
* 1. District block (absolute veto)
|
|
9
|
+
* 2. Test administration override
|
|
10
|
+
* 3. Item restriction (per-item block)
|
|
11
|
+
* 4. Item requirement (forces enable)
|
|
12
|
+
* 5. District requirement
|
|
13
|
+
* 6. PNP supports (student needs)
|
|
14
|
+
*
|
|
15
|
+
* Part of PIE Assessment Toolkit.
|
|
16
|
+
*/
|
|
17
|
+
import { mapPNPSupportToToolId } from "./PNPMapper";
|
|
18
|
+
/**
|
|
19
|
+
* Resolves tool availability from QTI 3.0 assessment structure
|
|
20
|
+
*/
|
|
21
|
+
export class PNPToolResolver {
|
|
22
|
+
/**
|
|
23
|
+
* Resolve all tools from QTI 3.0 assessment + optional item context
|
|
24
|
+
*
|
|
25
|
+
* @param assessment QTI 3.0 assessment with personalNeedsProfile
|
|
26
|
+
* @param currentItemRef Current item for item-specific requirements (optional)
|
|
27
|
+
* @returns Array of resolved tool configurations
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const resolver = new PNPToolResolver();
|
|
31
|
+
* const tools = resolver.resolveTools(assessment, itemRef);
|
|
32
|
+
* tools.forEach(tool => {
|
|
33
|
+
* console.log(`${tool.id}: ${tool.enabled ? 'enabled' : 'disabled'}`);
|
|
34
|
+
* });
|
|
35
|
+
*/
|
|
36
|
+
resolveTools(assessment, currentItemRef) {
|
|
37
|
+
const pnp = assessment.personalNeedsProfile;
|
|
38
|
+
const settings = assessment.settings;
|
|
39
|
+
const itemSettings = currentItemRef?.settings;
|
|
40
|
+
// Collect all PNP support IDs mentioned anywhere
|
|
41
|
+
const allSupports = new Set();
|
|
42
|
+
pnp?.supports?.forEach((s) => allSupports.add(s));
|
|
43
|
+
settings?.districtPolicy?.blockedTools?.forEach((s) => allSupports.add(s));
|
|
44
|
+
settings?.districtPolicy?.requiredTools?.forEach((s) => allSupports.add(s));
|
|
45
|
+
itemSettings?.requiredTools?.forEach((s) => allSupports.add(s));
|
|
46
|
+
itemSettings?.restrictedTools?.forEach((s) => allSupports.add(s));
|
|
47
|
+
const resolved = [];
|
|
48
|
+
for (const supportId of allSupports) {
|
|
49
|
+
const config = this.resolveSupport(supportId, {
|
|
50
|
+
pnp,
|
|
51
|
+
districtPolicy: settings?.districtPolicy,
|
|
52
|
+
testAdmin: settings?.testAdministration,
|
|
53
|
+
itemSettings,
|
|
54
|
+
toolConfigs: settings?.toolConfigs,
|
|
55
|
+
});
|
|
56
|
+
if (config) {
|
|
57
|
+
resolved.push(config);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return resolved;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Resolve availability of a single PNP support
|
|
64
|
+
*
|
|
65
|
+
* Applies precedence rules to determine if support should be enabled.
|
|
66
|
+
*
|
|
67
|
+
* @param supportId PNP support identifier
|
|
68
|
+
* @param context Resolution context with all configuration sources
|
|
69
|
+
* @returns Resolved config or null if blocked/disabled
|
|
70
|
+
*/
|
|
71
|
+
resolveSupport(supportId, context) {
|
|
72
|
+
// Precedence (highest to lowest):
|
|
73
|
+
// 1. District block (absolute veto)
|
|
74
|
+
if (context.districtPolicy?.blockedTools?.includes(supportId)) {
|
|
75
|
+
return null; // Blocked
|
|
76
|
+
}
|
|
77
|
+
// 2. Test administration override
|
|
78
|
+
if (context.testAdmin?.toolOverrides?.[supportId] === false) {
|
|
79
|
+
return null; // Blocked
|
|
80
|
+
}
|
|
81
|
+
// 3. Item restriction
|
|
82
|
+
if (context.itemSettings?.restrictedTools?.includes(supportId)) {
|
|
83
|
+
return null; // Blocked for this item
|
|
84
|
+
}
|
|
85
|
+
// 4. Item requirement (forces enable)
|
|
86
|
+
if (context.itemSettings?.requiredTools?.includes(supportId)) {
|
|
87
|
+
return this.buildToolConfig(supportId, context, {
|
|
88
|
+
required: true,
|
|
89
|
+
source: "item",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
// 5. District requirement
|
|
93
|
+
if (context.districtPolicy?.requiredTools?.includes(supportId)) {
|
|
94
|
+
return this.buildToolConfig(supportId, context, {
|
|
95
|
+
required: true,
|
|
96
|
+
source: "district",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
// 6. PNP supports (student needs)
|
|
100
|
+
if (context.pnp?.supports?.includes(supportId)) {
|
|
101
|
+
// Check if prohibited
|
|
102
|
+
const isProhibited = context.pnp.prohibitedSupports?.includes(supportId);
|
|
103
|
+
if (!isProhibited) {
|
|
104
|
+
return this.buildToolConfig(supportId, context, {
|
|
105
|
+
alwaysAvailable: true,
|
|
106
|
+
source: "pnp",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Not enabled
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Build resolved tool configuration
|
|
115
|
+
*
|
|
116
|
+
* Maps PNP support ID to PIE tool ID and merges tool-specific settings.
|
|
117
|
+
*
|
|
118
|
+
* @param supportId PNP support identifier
|
|
119
|
+
* @param context Resolution context for settings lookup
|
|
120
|
+
* @param flags Availability flags (required, alwaysAvailable, source)
|
|
121
|
+
* @returns Complete resolved tool configuration
|
|
122
|
+
*/
|
|
123
|
+
buildToolConfig(supportId, context, flags) {
|
|
124
|
+
// Map PNP support ID to PIE tool ID
|
|
125
|
+
const pieToolId = mapPNPSupportToToolId(supportId) || supportId;
|
|
126
|
+
// Get tool-specific config from settings (item takes precedence)
|
|
127
|
+
const toolConfig = context.itemSettings?.toolParameters?.[supportId] ||
|
|
128
|
+
context.toolConfigs?.[supportId];
|
|
129
|
+
return {
|
|
130
|
+
id: pieToolId,
|
|
131
|
+
enabled: true,
|
|
132
|
+
required: flags.required || false,
|
|
133
|
+
alwaysAvailable: flags.alwaysAvailable || false,
|
|
134
|
+
settings: toolConfig,
|
|
135
|
+
source: flags.source,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Check if a specific tool is enabled
|
|
140
|
+
*
|
|
141
|
+
* @param toolId PIE tool identifier (e.g., 'pie-tool-calculator')
|
|
142
|
+
* @param assessment Assessment with PNP
|
|
143
|
+
* @param itemRef Optional item context
|
|
144
|
+
* @returns true if tool is enabled
|
|
145
|
+
*/
|
|
146
|
+
isToolEnabled(toolId, assessment, itemRef) {
|
|
147
|
+
const resolved = this.resolveTools(assessment, itemRef);
|
|
148
|
+
return resolved.some((t) => t.id === toolId && t.enabled);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Check if a tool is required (cannot be disabled)
|
|
152
|
+
*
|
|
153
|
+
* @param toolId PIE tool identifier
|
|
154
|
+
* @param assessment Assessment with PNP
|
|
155
|
+
* @param itemRef Optional item context
|
|
156
|
+
* @returns true if tool is required or always available
|
|
157
|
+
*/
|
|
158
|
+
isToolRequired(toolId, assessment, itemRef) {
|
|
159
|
+
const resolved = this.resolveTools(assessment, itemRef);
|
|
160
|
+
const tool = resolved.find((t) => t.id === toolId);
|
|
161
|
+
return tool?.required || tool?.alwaysAvailable || false;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get list of auto-activate tools from PNP
|
|
165
|
+
*
|
|
166
|
+
* Returns PIE tool IDs that should be automatically opened/activated
|
|
167
|
+
* when the assessment starts, based on PNP activateAtInit field.
|
|
168
|
+
*
|
|
169
|
+
* @param assessment Assessment with PNP
|
|
170
|
+
* @returns Array of PIE tool IDs to auto-activate
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* const autoActivate = resolver.getAutoActivateTools(assessment);
|
|
174
|
+
* // Returns: ['pie-tool-text-to-speech', 'pie-tool-line-reader']
|
|
175
|
+
*/
|
|
176
|
+
getAutoActivateTools(assessment) {
|
|
177
|
+
const pnp = assessment.personalNeedsProfile;
|
|
178
|
+
if (!pnp?.activateAtInit)
|
|
179
|
+
return [];
|
|
180
|
+
return pnp.activateAtInit
|
|
181
|
+
.map((supportId) => mapPNPSupportToToolId(supportId))
|
|
182
|
+
.filter(Boolean);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get all enabled tools
|
|
186
|
+
*
|
|
187
|
+
* @param assessment Assessment with PNP
|
|
188
|
+
* @param itemRef Optional item context
|
|
189
|
+
* @returns Array of PIE tool IDs that are enabled
|
|
190
|
+
*/
|
|
191
|
+
getEnabledTools(assessment, itemRef) {
|
|
192
|
+
const resolved = this.resolveTools(assessment, itemRef);
|
|
193
|
+
return resolved.filter((t) => t.enabled).map((t) => t.id);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Get all required tools
|
|
197
|
+
*
|
|
198
|
+
* @param assessment Assessment with PNP
|
|
199
|
+
* @param itemRef Optional item context
|
|
200
|
+
* @returns Array of PIE tool IDs that are required
|
|
201
|
+
*/
|
|
202
|
+
getRequiredTools(assessment, itemRef) {
|
|
203
|
+
const resolved = this.resolveTools(assessment, itemRef);
|
|
204
|
+
return resolved
|
|
205
|
+
.filter((t) => t.required || t.alwaysAvailable)
|
|
206
|
+
.map((t) => t.id);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Get tool configuration settings
|
|
210
|
+
*
|
|
211
|
+
* @param toolId PIE tool identifier
|
|
212
|
+
* @param assessment Assessment with PNP
|
|
213
|
+
* @param itemRef Optional item context
|
|
214
|
+
* @returns Tool-specific settings or null if not found
|
|
215
|
+
*/
|
|
216
|
+
getToolSettings(toolId, assessment, itemRef) {
|
|
217
|
+
const resolved = this.resolveTools(assessment, itemRef);
|
|
218
|
+
const tool = resolved.find((t) => t.id === toolId);
|
|
219
|
+
return tool?.settings || null;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=PNPToolResolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PNPToolResolver.js","sourceRoot":"","sources":["../../src/services/PNPToolResolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSMLExtractor
|
|
3
|
+
*
|
|
4
|
+
* Service for extracting embedded SSML from PIE item content and converting
|
|
5
|
+
* it into QTI 3.0 accessibility catalogs.
|
|
6
|
+
*
|
|
7
|
+
* Authors can embed <speak> SSML tags directly in content for convenience.
|
|
8
|
+
* The extractor automatically:
|
|
9
|
+
* 1. Parses markup and finds all <speak> elements
|
|
10
|
+
* 2. Extracts SSML content with language metadata
|
|
11
|
+
* 3. Generates accessibility catalogs with unique IDs
|
|
12
|
+
* 4. Cleans visual markup (removes SSML, adds data-catalog-id)
|
|
13
|
+
*
|
|
14
|
+
* This enables proper pronunciation, emphasis, and pacing for TTS without
|
|
15
|
+
* requiring authors to maintain separate catalog files.
|
|
16
|
+
*
|
|
17
|
+
* Part of PIE Assessment Toolkit.
|
|
18
|
+
*/
|
|
19
|
+
import type { AccessibilityCatalog, ConfigEntity } from "@pie-players/pie-players-shared/types";
|
|
20
|
+
/**
|
|
21
|
+
* Result from extracting SSML from entire item config
|
|
22
|
+
*/
|
|
23
|
+
export interface ExtractionResult {
|
|
24
|
+
catalogs: AccessibilityCatalog[];
|
|
25
|
+
cleanedConfig: ConfigEntity;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* SSMLExtractor Service
|
|
29
|
+
*
|
|
30
|
+
* Extracts embedded SSML from PIE content and generates accessibility catalogs.
|
|
31
|
+
*/
|
|
32
|
+
export declare class SSMLExtractor {
|
|
33
|
+
private catalogCounter;
|
|
34
|
+
/**
|
|
35
|
+
* Extract SSML from entire item config (markup + models)
|
|
36
|
+
*
|
|
37
|
+
* @param config Item configuration with potential embedded SSML
|
|
38
|
+
* @returns Extracted catalogs and cleaned config
|
|
39
|
+
*/
|
|
40
|
+
extractFromItemConfig(config: ConfigEntity): ExtractionResult;
|
|
41
|
+
/**
|
|
42
|
+
* Extract SSML from markup string
|
|
43
|
+
*
|
|
44
|
+
* @param markup HTML markup potentially containing <speak> elements
|
|
45
|
+
* @param idPrefix Prefix for generating catalog IDs
|
|
46
|
+
* @returns Extracted catalogs and cleaned markup
|
|
47
|
+
*/
|
|
48
|
+
private extractFromMarkup;
|
|
49
|
+
/**
|
|
50
|
+
* Generate unique catalog ID
|
|
51
|
+
*
|
|
52
|
+
* @param prefix Context prefix (e.g., 'prompt-q1', 'choice-q1-a')
|
|
53
|
+
* @returns Unique catalog ID (e.g., 'auto-prompt-q1-0')
|
|
54
|
+
*/
|
|
55
|
+
private generateCatalogId;
|
|
56
|
+
/**
|
|
57
|
+
* Reset counter (useful for testing or new extraction contexts)
|
|
58
|
+
*/
|
|
59
|
+
reset(): void;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=SSMLExtractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SSMLExtractor.d.ts","sourceRoot":"","sources":["../../src/services/SSMLExtractor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EACX,oBAAoB,EACpB,YAAY,EAEZ,MAAM,uCAAuC,CAAC;AAU/C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,oBAAoB,EAAE,CAAC;IACjC,aAAa,EAAE,YAAY,CAAC;CAC5B;AAED;;;;GAIG;AACH,qBAAa,aAAa;IACzB,OAAO,CAAC,cAAc,CAAK;IAE3B;;;;;OAKG;IACH,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,gBAAgB;IA6D7D;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAmGzB;;;;;OAKG;IACH,OAAO,CAAC,iBAAiB;IAMzB;;OAEG;IACH,KAAK,IAAI,IAAI;CAGb"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSMLExtractor
|
|
3
|
+
*
|
|
4
|
+
* Service for extracting embedded SSML from PIE item content and converting
|
|
5
|
+
* it into QTI 3.0 accessibility catalogs.
|
|
6
|
+
*
|
|
7
|
+
* Authors can embed <speak> SSML tags directly in content for convenience.
|
|
8
|
+
* The extractor automatically:
|
|
9
|
+
* 1. Parses markup and finds all <speak> elements
|
|
10
|
+
* 2. Extracts SSML content with language metadata
|
|
11
|
+
* 3. Generates accessibility catalogs with unique IDs
|
|
12
|
+
* 4. Cleans visual markup (removes SSML, adds data-catalog-id)
|
|
13
|
+
*
|
|
14
|
+
* This enables proper pronunciation, emphasis, and pacing for TTS without
|
|
15
|
+
* requiring authors to maintain separate catalog files.
|
|
16
|
+
*
|
|
17
|
+
* Part of PIE Assessment Toolkit.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* SSMLExtractor Service
|
|
21
|
+
*
|
|
22
|
+
* Extracts embedded SSML from PIE content and generates accessibility catalogs.
|
|
23
|
+
*/
|
|
24
|
+
export class SSMLExtractor {
|
|
25
|
+
catalogCounter = 0;
|
|
26
|
+
/**
|
|
27
|
+
* Extract SSML from entire item config (markup + models)
|
|
28
|
+
*
|
|
29
|
+
* @param config Item configuration with potential embedded SSML
|
|
30
|
+
* @returns Extracted catalogs and cleaned config
|
|
31
|
+
*/
|
|
32
|
+
extractFromItemConfig(config) {
|
|
33
|
+
const allCatalogs = [];
|
|
34
|
+
// Deep clone config to avoid mutating original
|
|
35
|
+
const cleanedConfig = {
|
|
36
|
+
...config,
|
|
37
|
+
models: config.models ? [...config.models] : [],
|
|
38
|
+
};
|
|
39
|
+
// 1. Extract from top-level markup (passages)
|
|
40
|
+
if (config.markup) {
|
|
41
|
+
const markupResult = this.extractFromMarkup(config.markup, "markup");
|
|
42
|
+
allCatalogs.push(...markupResult.catalogs);
|
|
43
|
+
cleanedConfig.markup = markupResult.cleanedMarkup;
|
|
44
|
+
}
|
|
45
|
+
// 2. Extract from models (prompts, choices, etc.)
|
|
46
|
+
if (config.models && config.models.length > 0) {
|
|
47
|
+
cleanedConfig.models = config.models.map((model) => {
|
|
48
|
+
const cleanedModel = { ...model };
|
|
49
|
+
const modelCatalogs = [];
|
|
50
|
+
// Extract from prompt
|
|
51
|
+
if (model.prompt && typeof model.prompt === "string") {
|
|
52
|
+
const promptResult = this.extractFromMarkup(model.prompt, `prompt-${model.id}`);
|
|
53
|
+
modelCatalogs.push(...promptResult.catalogs);
|
|
54
|
+
cleanedModel.prompt = promptResult.cleanedMarkup;
|
|
55
|
+
}
|
|
56
|
+
// Extract from choices
|
|
57
|
+
if (model.choices && Array.isArray(model.choices)) {
|
|
58
|
+
cleanedModel.choices = model.choices.map((choice) => {
|
|
59
|
+
if (choice.label && typeof choice.label === "string") {
|
|
60
|
+
const choiceResult = this.extractFromMarkup(choice.label, `choice-${model.id}-${choice.value}`);
|
|
61
|
+
modelCatalogs.push(...choiceResult.catalogs);
|
|
62
|
+
return {
|
|
63
|
+
...choice,
|
|
64
|
+
label: choiceResult.cleanedMarkup,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return choice;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
allCatalogs.push(...modelCatalogs);
|
|
71
|
+
return cleanedModel;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
catalogs: allCatalogs,
|
|
76
|
+
cleanedConfig,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Extract SSML from markup string
|
|
81
|
+
*
|
|
82
|
+
* @param markup HTML markup potentially containing <speak> elements
|
|
83
|
+
* @param idPrefix Prefix for generating catalog IDs
|
|
84
|
+
* @returns Extracted catalogs and cleaned markup
|
|
85
|
+
*/
|
|
86
|
+
extractFromMarkup(markup, idPrefix) {
|
|
87
|
+
if (!markup || markup.trim() === "") {
|
|
88
|
+
return { catalogs: [], cleanedMarkup: markup };
|
|
89
|
+
}
|
|
90
|
+
// Check if running in browser environment
|
|
91
|
+
if (typeof window === "undefined" || typeof DOMParser === "undefined") {
|
|
92
|
+
console.warn("[SSMLExtractor] DOMParser not available (SSR?), skipping extraction");
|
|
93
|
+
return { catalogs: [], cleanedMarkup: markup };
|
|
94
|
+
}
|
|
95
|
+
const catalogs = [];
|
|
96
|
+
try {
|
|
97
|
+
const parser = new DOMParser();
|
|
98
|
+
const doc = parser.parseFromString(markup, "text/html");
|
|
99
|
+
// Find all <speak> elements
|
|
100
|
+
const speakElements = Array.from(doc.querySelectorAll("speak"));
|
|
101
|
+
if (speakElements.length === 0) {
|
|
102
|
+
// No SSML found, return original markup
|
|
103
|
+
return { catalogs: [], cleanedMarkup: markup };
|
|
104
|
+
}
|
|
105
|
+
// Process each <speak> element
|
|
106
|
+
for (const speakEl of speakElements) {
|
|
107
|
+
try {
|
|
108
|
+
// Generate unique catalog ID
|
|
109
|
+
const catalogId = this.generateCatalogId(idPrefix);
|
|
110
|
+
// Extract SSML content
|
|
111
|
+
const ssmlContent = speakEl.outerHTML;
|
|
112
|
+
// Extract language (check both xml:lang and lang attributes)
|
|
113
|
+
const language = speakEl.getAttribute("xml:lang") ||
|
|
114
|
+
speakEl.getAttribute("lang") ||
|
|
115
|
+
"en-US";
|
|
116
|
+
// Get plain text for visual display
|
|
117
|
+
const plainText = speakEl.textContent || "";
|
|
118
|
+
// Find parent element or create wrapper
|
|
119
|
+
let wrapper = speakEl.parentElement;
|
|
120
|
+
if (!wrapper || wrapper.tagName === "BODY") {
|
|
121
|
+
// SPEAK is at root level - create span wrapper
|
|
122
|
+
const span = doc.createElement("span");
|
|
123
|
+
speakEl.parentNode?.insertBefore(span, speakEl);
|
|
124
|
+
span.appendChild(doc.createTextNode(plainText));
|
|
125
|
+
wrapper = span;
|
|
126
|
+
speakEl.remove();
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
// Just remove <speak> - visual content should be in sibling element
|
|
130
|
+
speakEl.remove();
|
|
131
|
+
}
|
|
132
|
+
// Add catalog ID to wrapper element
|
|
133
|
+
if (wrapper) {
|
|
134
|
+
wrapper.setAttribute("data-catalog-id", catalogId);
|
|
135
|
+
}
|
|
136
|
+
// Create catalog entry
|
|
137
|
+
catalogs.push({
|
|
138
|
+
identifier: catalogId,
|
|
139
|
+
cards: [
|
|
140
|
+
{
|
|
141
|
+
catalog: "spoken",
|
|
142
|
+
language,
|
|
143
|
+
content: ssmlContent,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error("[SSMLExtractor] Error processing <speak> element:", error);
|
|
150
|
+
// Continue with other elements
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Return cleaned markup
|
|
154
|
+
const cleanedMarkup = doc.body.innerHTML;
|
|
155
|
+
return { catalogs, cleanedMarkup };
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
console.error("[SSMLExtractor] Error parsing markup:", error);
|
|
159
|
+
// Return original markup if parsing fails
|
|
160
|
+
return { catalogs: [], cleanedMarkup: markup };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Generate unique catalog ID
|
|
165
|
+
*
|
|
166
|
+
* @param prefix Context prefix (e.g., 'prompt-q1', 'choice-q1-a')
|
|
167
|
+
* @returns Unique catalog ID (e.g., 'auto-prompt-q1-0')
|
|
168
|
+
*/
|
|
169
|
+
generateCatalogId(prefix) {
|
|
170
|
+
const id = `auto-${prefix}-${this.catalogCounter}`;
|
|
171
|
+
this.catalogCounter++;
|
|
172
|
+
return id;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Reset counter (useful for testing or new extraction contexts)
|
|
176
|
+
*/
|
|
177
|
+
reset() {
|
|
178
|
+
this.catalogCounter = 0;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=SSMLExtractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SSMLExtractor.js","sourceRoot":"","sources":["../../src/services/SSMLExtractor.ts"],"names":[],"mappings":"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"}
|