@pie-players/pie-assessment-toolkit 0.3.72 → 0.3.74
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 +171 -56
- package/dist/attempt/AssessmentSession.d.ts +1 -5
- package/dist/attempt/AssessmentSession.js +0 -29
- package/dist/components/ItemToolBar.custom-element.d.ts +1 -0
- package/dist/components/ItemToolBar.custom-element.js +1 -1
- package/dist/components/PieAssessmentToolkit.custom-element.d.ts +1 -0
- package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
- package/dist/components/SectionToolBar.custom-element.d.ts +1 -0
- package/dist/components/SectionToolBar.custom-element.js +1 -1
- package/dist/components/chunks/ItemToolBar-b9dz5jvd.js +40 -0
- package/dist/components/chunks/ItemToolBar-gmrryv2v.js +2 -0
- package/dist/components/chunks/ItemToolBar-h4xap0t0.js +4 -0
- package/dist/components/chunks/ItemToolBar-wr2myaah.js +2 -0
- package/dist/components/chunks/sre-engine-wf9s43gg.js +1 -0
- package/dist/context/assessment-toolkit-context.d.ts +5 -0
- package/dist/context/runtime-context-consumer.js +5 -38
- package/dist/index.d.ts +6 -6
- package/dist/index.js +8 -9
- package/dist/policy/core/ToolPolicyEngine.d.ts +8 -2
- package/dist/policy/core/ToolPolicyEngine.js +12 -0
- package/dist/runtime/adapter/SectionEngineAdapter.d.ts +3 -3
- package/dist/runtime/adapter/SectionEngineAdapter.js +3 -3
- package/dist/runtime/registration-events.d.ts +3 -0
- package/dist/runtime/section-runtime-engine-host-context.d.ts +3 -9
- package/dist/runtime/section-runtime-engine-host-context.js +5 -43
- package/dist/runtime/tool-host-contract.d.ts +20 -0
- package/dist/runtime/tool-host-contract.js +21 -0
- package/dist/services/HighlightCoordinator.d.ts +1 -7
- package/dist/services/HighlightCoordinator.js +1 -3
- package/dist/services/TTSService.d.ts +15 -1
- package/dist/services/TTSService.js +88 -35
- package/dist/services/ToolCoordinator.d.ts +1 -7
- package/dist/services/ToolCoordinator.js +1 -3
- package/dist/services/ToolRegistry.d.ts +13 -6
- package/dist/services/ToolRegistry.js +19 -7
- package/dist/services/ToolkitCoordinator.d.ts +110 -18
- package/dist/services/ToolkitCoordinator.js +273 -85
- package/dist/services/catalog-owner.d.ts +11 -0
- package/dist/services/catalog-owner.js +25 -0
- package/dist/services/create-tools-config.d.ts +2 -2
- package/dist/services/framework-error.d.ts +8 -1
- package/dist/services/interfaces.d.ts +9 -9
- package/dist/services/pnp-standard-features.d.ts +1 -1
- package/dist/services/section-controller-types.d.ts +30 -0
- package/dist/services/spoken-audio-cards.js +1 -1
- package/dist/services/tool-config-validation.d.ts +13 -2
- package/dist/services/tool-config-validation.js +33 -16
- package/dist/services/tool-context.js +29 -13
- package/dist/services/tool-providers/TTSToolProvider.d.ts +27 -65
- package/dist/services/tool-providers/TTSToolProvider.js +47 -9
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts +4 -4
- package/dist/services/tool-providers/ToolProviderRegistry.js +4 -16
- package/dist/services/tool-providers/index.d.ts +0 -9
- package/dist/services/tool-providers/index.js +2 -4
- package/dist/services/tools-config-normalizer.d.ts +20 -1
- package/dist/services/tts/catalog-span-alignment.d.ts +2 -17
- package/dist/services/tts/catalog-span-alignment.js +17 -142
- package/dist/services/tts/generated-speech/types.d.ts +0 -12
- package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +12 -62
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +26 -1
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +3 -6
- package/dist/services/tts/math-alignment/index.d.ts +0 -5
- package/dist/services/tts/math-alignment/index.js +5 -25
- package/dist/services/tts/math-alignment/speech-tokenizer.d.ts +9 -1
- package/dist/services/tts/math-alignment/speech-tokenizer.js +38 -36
- package/dist/services/tts/math-alignment/types.d.ts +4 -2
- package/dist/services/tts/math-aware-text-processing.js +44 -2
- package/dist/services/tts/math-speech.d.ts +5 -1
- package/dist/services/tts/math-speech.js +9 -6
- package/dist/services/tts/sre-engine.d.ts +7 -0
- package/dist/services/tts/sre-engine.js +11 -0
- package/dist/services/tts/sre-locales.d.ts +14 -0
- package/dist/services/tts/sre-locales.js +57 -0
- package/dist/services/tts/text-processing.d.ts +0 -1
- package/dist/services/tts/text-processing.js +0 -1
- package/dist/services/tts-runtime-config.d.ts +21 -3
- package/dist/services/tts-runtime-config.js +11 -3
- package/dist/tools/client.d.ts +1 -3
- package/dist/tools/client.js +1 -5
- package/dist/tools/internal.d.ts +6 -5
- package/dist/tools/internal.js +6 -8
- package/package.json +17 -36
- package/dist/components/chunks/ItemToolBar-byq80z42.js +0 -40
- package/dist/components/chunks/ItemToolBar-d36g3aw4.js +0 -49
- package/dist/services/ToolConfigResolver.d.ts +0 -161
- package/dist/services/ToolConfigResolver.js +0 -229
- package/dist/services/catalog-media.d.ts +0 -56
- package/dist/services/catalog-media.js +0 -190
- package/dist/services/createDefaultToolRegistry.d.ts +0 -53
- package/dist/services/createDefaultToolRegistry.js +0 -45
- package/dist/services/tool-providers/CortexToolProvider.d.ts +0 -13
- package/dist/services/tool-providers/CortexToolProvider.js +0 -32
- package/dist/services/tool-providers/DesmosToolProvider.d.ts +0 -14
- package/dist/services/tool-providers/DesmosToolProvider.js +0 -32
- package/dist/services/tool-providers/GeoGebraToolProvider.d.ts +0 -16
- package/dist/services/tool-providers/GeoGebraToolProvider.js +0 -32
- package/dist/services/tool-providers/LazyCalculatorToolProvider.d.ts +0 -35
- package/dist/services/tool-providers/LazyCalculatorToolProvider.js +0 -95
- package/dist/services/tts/visual-line-ranges.d.ts +0 -18
- package/dist/services/tts/visual-line-ranges.js +0 -226
- package/dist/tools/index.d.ts +0 -8
- package/dist/tools/index.js +0 -10
- package/dist/tools/response-discovery.d.ts +0 -61
- package/dist/tools/response-discovery.js +0 -113
- package/dist/tools/types.d.ts +0 -337
- package/dist/tools/types.js +0 -12
- package/dist/tools/variant-resolver.d.ts +0 -41
- package/dist/tools/variant-resolver.js +0 -207
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ToolConfigResolver
|
|
3
|
-
*
|
|
4
|
-
* Implements 3-tier hierarchy for tool configuration:
|
|
5
|
-
* Item requirements > Roster allowances > Student accommodations
|
|
6
|
-
*
|
|
7
|
-
* Ensures IEP/504 compliance by properly handling accommodation requirements
|
|
8
|
-
* while respecting item-level and test-level restrictions.
|
|
9
|
-
*
|
|
10
|
-
* Features:
|
|
11
|
-
* - 3-tier configuration hierarchy
|
|
12
|
-
* - IEP/504 accommodation support
|
|
13
|
-
* - Item-specific tool requirements
|
|
14
|
-
* - Test-level tool restrictions
|
|
15
|
-
* - Type-safe configuration
|
|
16
|
-
*
|
|
17
|
-
* Part of PIE Assessment Toolkit.
|
|
18
|
-
*/
|
|
19
|
-
/**
|
|
20
|
-
* Tool allowance: "0" = blocked, "1" = allowed
|
|
21
|
-
*/
|
|
22
|
-
export type ToolAllowance = "0" | "1";
|
|
23
|
-
/**
|
|
24
|
-
* Item-level tool configuration (most specific)
|
|
25
|
-
*/
|
|
26
|
-
export interface ItemToolConfig {
|
|
27
|
-
[toolId: string]: {
|
|
28
|
-
type?: string;
|
|
29
|
-
required?: boolean;
|
|
30
|
-
settings?: any;
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Roster/Test-level tool allowances (assessment-wide rules)
|
|
35
|
-
*/
|
|
36
|
-
export interface RosterToolConfig {
|
|
37
|
-
[toolId: string]: ToolAllowance;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Student accommodation profile (least specific)
|
|
41
|
-
*/
|
|
42
|
-
export interface StudentAccommodations {
|
|
43
|
-
accommodations: string[];
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Resolved tool configuration
|
|
47
|
-
*/
|
|
48
|
-
export interface ResolvedToolConfig {
|
|
49
|
-
enabled: boolean;
|
|
50
|
-
type?: string;
|
|
51
|
-
required?: boolean;
|
|
52
|
-
settings?: any;
|
|
53
|
-
source: "item" | "roster" | "student" | "default";
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Full configuration input
|
|
57
|
-
*/
|
|
58
|
-
export interface ToolConfigInput {
|
|
59
|
-
itemConfig?: ItemToolConfig;
|
|
60
|
-
rosterConfig?: RosterToolConfig;
|
|
61
|
-
studentProfile?: StudentAccommodations;
|
|
62
|
-
}
|
|
63
|
-
export declare class ToolConfigResolver {
|
|
64
|
-
/**
|
|
65
|
-
* Resolve configuration for a specific tool
|
|
66
|
-
*
|
|
67
|
-
* Hierarchy (highest to lowest priority):
|
|
68
|
-
* 1. Item level: Item requires scientific calculator → must provide
|
|
69
|
-
* 2. Roster level: Test blocks a tool → blocked regardless of accommodation
|
|
70
|
-
* 3. Student level: IEP requires TTS → enabled unless blocked above
|
|
71
|
-
*
|
|
72
|
-
* @param toolId Tool identifier
|
|
73
|
-
* @param itemConfig Item-level tool configuration
|
|
74
|
-
* @param rosterConfig Roster-level tool allowances
|
|
75
|
-
* @param studentProfile Student accommodation profile
|
|
76
|
-
* @returns Resolved configuration or null if tool not available
|
|
77
|
-
*/
|
|
78
|
-
resolveTool(toolId: string, itemConfig?: ItemToolConfig, rosterConfig?: RosterToolConfig, studentProfile?: StudentAccommodations): ResolvedToolConfig | null;
|
|
79
|
-
/**
|
|
80
|
-
* Resolve all tools from configuration
|
|
81
|
-
*
|
|
82
|
-
* @param input Configuration input
|
|
83
|
-
* @returns Map of tool ID to resolved configuration
|
|
84
|
-
*/
|
|
85
|
-
resolveAll(input: ToolConfigInput): Map<string, ResolvedToolConfig>;
|
|
86
|
-
/**
|
|
87
|
-
* Check if a tool is enabled
|
|
88
|
-
*
|
|
89
|
-
* @param toolId Tool identifier
|
|
90
|
-
* @param input Configuration input
|
|
91
|
-
* @returns true if tool is enabled
|
|
92
|
-
*/
|
|
93
|
-
isToolEnabled(toolId: string, input: ToolConfigInput): boolean;
|
|
94
|
-
/**
|
|
95
|
-
* Check if a tool is required
|
|
96
|
-
*
|
|
97
|
-
* @param toolId Tool identifier
|
|
98
|
-
* @param input Configuration input
|
|
99
|
-
* @returns true if tool is required
|
|
100
|
-
*/
|
|
101
|
-
isToolRequired(toolId: string, input: ToolConfigInput): boolean;
|
|
102
|
-
/**
|
|
103
|
-
* Get tool type (e.g., 'scientific' for calculator)
|
|
104
|
-
*
|
|
105
|
-
* @param toolId Tool identifier
|
|
106
|
-
* @param input Configuration input
|
|
107
|
-
* @returns Tool type or null
|
|
108
|
-
*/
|
|
109
|
-
getToolType(toolId: string, input: ToolConfigInput): string | null;
|
|
110
|
-
/**
|
|
111
|
-
* Get tool settings
|
|
112
|
-
*
|
|
113
|
-
* @param toolId Tool identifier
|
|
114
|
-
* @param input Configuration input
|
|
115
|
-
* @returns Tool settings or null
|
|
116
|
-
*/
|
|
117
|
-
getToolSettings(toolId: string, input: ToolConfigInput): any | null;
|
|
118
|
-
/**
|
|
119
|
-
* Get list of all enabled tools
|
|
120
|
-
*
|
|
121
|
-
* @param input Configuration input
|
|
122
|
-
* @returns Array of enabled tool IDs
|
|
123
|
-
*/
|
|
124
|
-
getEnabledTools(input: ToolConfigInput): string[];
|
|
125
|
-
/**
|
|
126
|
-
* Get list of required tools
|
|
127
|
-
*
|
|
128
|
-
* @param input Configuration input
|
|
129
|
-
* @returns Array of required tool IDs
|
|
130
|
-
*/
|
|
131
|
-
getRequiredTools(input: ToolConfigInput): string[];
|
|
132
|
-
/**
|
|
133
|
-
* Validate that all required tools are available
|
|
134
|
-
*
|
|
135
|
-
* @param availableTools Set of available tool IDs
|
|
136
|
-
* @param input Configuration input
|
|
137
|
-
* @returns Object with validation result
|
|
138
|
-
*/
|
|
139
|
-
validate(availableTools: Set<string>, input: ToolConfigInput): {
|
|
140
|
-
valid: boolean;
|
|
141
|
-
missingTools: string[];
|
|
142
|
-
};
|
|
143
|
-
/**
|
|
144
|
-
* Merge multiple roster configurations (for complex assessments)
|
|
145
|
-
*
|
|
146
|
-
* More restrictive settings win (blocked > allowed).
|
|
147
|
-
*
|
|
148
|
-
* @param configs Array of roster configurations
|
|
149
|
-
* @returns Merged configuration
|
|
150
|
-
*/
|
|
151
|
-
mergeRosterConfigs(...configs: RosterToolConfig[]): RosterToolConfig;
|
|
152
|
-
/**
|
|
153
|
-
* Merge multiple student accommodation profiles
|
|
154
|
-
*
|
|
155
|
-
* Union of all accommodations.
|
|
156
|
-
*
|
|
157
|
-
* @param profiles Array of student profiles
|
|
158
|
-
* @returns Merged profile
|
|
159
|
-
*/
|
|
160
|
-
mergeStudentProfiles(...profiles: StudentAccommodations[]): StudentAccommodations;
|
|
161
|
-
}
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ToolConfigResolver
|
|
3
|
-
*
|
|
4
|
-
* Implements 3-tier hierarchy for tool configuration:
|
|
5
|
-
* Item requirements > Roster allowances > Student accommodations
|
|
6
|
-
*
|
|
7
|
-
* Ensures IEP/504 compliance by properly handling accommodation requirements
|
|
8
|
-
* while respecting item-level and test-level restrictions.
|
|
9
|
-
*
|
|
10
|
-
* Features:
|
|
11
|
-
* - 3-tier configuration hierarchy
|
|
12
|
-
* - IEP/504 accommodation support
|
|
13
|
-
* - Item-specific tool requirements
|
|
14
|
-
* - Test-level tool restrictions
|
|
15
|
-
* - Type-safe configuration
|
|
16
|
-
*
|
|
17
|
-
* Part of PIE Assessment Toolkit.
|
|
18
|
-
*/
|
|
19
|
-
export class ToolConfigResolver {
|
|
20
|
-
/**
|
|
21
|
-
* Resolve configuration for a specific tool
|
|
22
|
-
*
|
|
23
|
-
* Hierarchy (highest to lowest priority):
|
|
24
|
-
* 1. Item level: Item requires scientific calculator → must provide
|
|
25
|
-
* 2. Roster level: Test blocks a tool → blocked regardless of accommodation
|
|
26
|
-
* 3. Student level: IEP requires TTS → enabled unless blocked above
|
|
27
|
-
*
|
|
28
|
-
* @param toolId Tool identifier
|
|
29
|
-
* @param itemConfig Item-level tool configuration
|
|
30
|
-
* @param rosterConfig Roster-level tool allowances
|
|
31
|
-
* @param studentProfile Student accommodation profile
|
|
32
|
-
* @returns Resolved configuration or null if tool not available
|
|
33
|
-
*/
|
|
34
|
-
resolveTool(toolId, itemConfig, rosterConfig, studentProfile) {
|
|
35
|
-
// Check item level (highest priority)
|
|
36
|
-
const itemTool = itemConfig?.[toolId];
|
|
37
|
-
if (itemTool) {
|
|
38
|
-
// Item explicitly configures this tool
|
|
39
|
-
return {
|
|
40
|
-
enabled: true,
|
|
41
|
-
type: itemTool.type,
|
|
42
|
-
required: itemTool.required ?? false,
|
|
43
|
-
settings: itemTool.settings,
|
|
44
|
-
source: "item",
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
// Check roster level (blocks take precedence)
|
|
48
|
-
const rosterAllowance = rosterConfig?.[toolId];
|
|
49
|
-
if (rosterAllowance === "0") {
|
|
50
|
-
// Roster explicitly blocks this tool
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
if (rosterAllowance === "1") {
|
|
54
|
-
// Roster allows this tool
|
|
55
|
-
return {
|
|
56
|
-
enabled: true,
|
|
57
|
-
required: false,
|
|
58
|
-
source: "roster",
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
// Check student level (lowest priority)
|
|
62
|
-
const hasAccommodation = studentProfile?.accommodations.includes(toolId) ?? false;
|
|
63
|
-
if (hasAccommodation) {
|
|
64
|
-
// Student has accommodation for this tool
|
|
65
|
-
return {
|
|
66
|
-
enabled: true,
|
|
67
|
-
required: false,
|
|
68
|
-
source: "student",
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
// Tool not configured at any level
|
|
72
|
-
return null;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Resolve all tools from configuration
|
|
76
|
-
*
|
|
77
|
-
* @param input Configuration input
|
|
78
|
-
* @returns Map of tool ID to resolved configuration
|
|
79
|
-
*/
|
|
80
|
-
resolveAll(input) {
|
|
81
|
-
const resolved = new Map();
|
|
82
|
-
// Collect all tool IDs from all levels
|
|
83
|
-
const allToolIds = new Set();
|
|
84
|
-
// From item config
|
|
85
|
-
if (input.itemConfig) {
|
|
86
|
-
Object.keys(input.itemConfig).forEach((id) => allToolIds.add(id));
|
|
87
|
-
}
|
|
88
|
-
// From roster config
|
|
89
|
-
if (input.rosterConfig) {
|
|
90
|
-
Object.keys(input.rosterConfig).forEach((id) => allToolIds.add(id));
|
|
91
|
-
}
|
|
92
|
-
// From student accommodations
|
|
93
|
-
if (input.studentProfile) {
|
|
94
|
-
input.studentProfile.accommodations.forEach((id) => allToolIds.add(id));
|
|
95
|
-
}
|
|
96
|
-
// Resolve each tool
|
|
97
|
-
for (const toolId of allToolIds) {
|
|
98
|
-
const config = this.resolveTool(toolId, input.itemConfig, input.rosterConfig, input.studentProfile);
|
|
99
|
-
if (config) {
|
|
100
|
-
resolved.set(toolId, config);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return resolved;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Check if a tool is enabled
|
|
107
|
-
*
|
|
108
|
-
* @param toolId Tool identifier
|
|
109
|
-
* @param input Configuration input
|
|
110
|
-
* @returns true if tool is enabled
|
|
111
|
-
*/
|
|
112
|
-
isToolEnabled(toolId, input) {
|
|
113
|
-
const config = this.resolveTool(toolId, input.itemConfig, input.rosterConfig, input.studentProfile);
|
|
114
|
-
return config?.enabled ?? false;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Check if a tool is required
|
|
118
|
-
*
|
|
119
|
-
* @param toolId Tool identifier
|
|
120
|
-
* @param input Configuration input
|
|
121
|
-
* @returns true if tool is required
|
|
122
|
-
*/
|
|
123
|
-
isToolRequired(toolId, input) {
|
|
124
|
-
const config = this.resolveTool(toolId, input.itemConfig, input.rosterConfig, input.studentProfile);
|
|
125
|
-
return config?.required ?? false;
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Get tool type (e.g., 'scientific' for calculator)
|
|
129
|
-
*
|
|
130
|
-
* @param toolId Tool identifier
|
|
131
|
-
* @param input Configuration input
|
|
132
|
-
* @returns Tool type or null
|
|
133
|
-
*/
|
|
134
|
-
getToolType(toolId, input) {
|
|
135
|
-
const config = this.resolveTool(toolId, input.itemConfig, input.rosterConfig, input.studentProfile);
|
|
136
|
-
return config?.type ?? null;
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Get tool settings
|
|
140
|
-
*
|
|
141
|
-
* @param toolId Tool identifier
|
|
142
|
-
* @param input Configuration input
|
|
143
|
-
* @returns Tool settings or null
|
|
144
|
-
*/
|
|
145
|
-
getToolSettings(toolId, input) {
|
|
146
|
-
const config = this.resolveTool(toolId, input.itemConfig, input.rosterConfig, input.studentProfile);
|
|
147
|
-
return config?.settings ?? null;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Get list of all enabled tools
|
|
151
|
-
*
|
|
152
|
-
* @param input Configuration input
|
|
153
|
-
* @returns Array of enabled tool IDs
|
|
154
|
-
*/
|
|
155
|
-
getEnabledTools(input) {
|
|
156
|
-
const resolved = this.resolveAll(input);
|
|
157
|
-
return Array.from(resolved.entries())
|
|
158
|
-
.filter(([_, config]) => config.enabled)
|
|
159
|
-
.map(([toolId]) => toolId);
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Get list of required tools
|
|
163
|
-
*
|
|
164
|
-
* @param input Configuration input
|
|
165
|
-
* @returns Array of required tool IDs
|
|
166
|
-
*/
|
|
167
|
-
getRequiredTools(input) {
|
|
168
|
-
const resolved = this.resolveAll(input);
|
|
169
|
-
return Array.from(resolved.entries())
|
|
170
|
-
.filter(([_, config]) => config.required)
|
|
171
|
-
.map(([toolId]) => toolId);
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Validate that all required tools are available
|
|
175
|
-
*
|
|
176
|
-
* @param availableTools Set of available tool IDs
|
|
177
|
-
* @param input Configuration input
|
|
178
|
-
* @returns Object with validation result
|
|
179
|
-
*/
|
|
180
|
-
validate(availableTools, input) {
|
|
181
|
-
const requiredTools = this.getRequiredTools(input);
|
|
182
|
-
const missingTools = requiredTools.filter((toolId) => !availableTools.has(toolId));
|
|
183
|
-
return {
|
|
184
|
-
valid: missingTools.length === 0,
|
|
185
|
-
missingTools,
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Merge multiple roster configurations (for complex assessments)
|
|
190
|
-
*
|
|
191
|
-
* More restrictive settings win (blocked > allowed).
|
|
192
|
-
*
|
|
193
|
-
* @param configs Array of roster configurations
|
|
194
|
-
* @returns Merged configuration
|
|
195
|
-
*/
|
|
196
|
-
mergeRosterConfigs(...configs) {
|
|
197
|
-
const merged = {};
|
|
198
|
-
for (const config of configs) {
|
|
199
|
-
for (const [toolId, allowance] of Object.entries(config)) {
|
|
200
|
-
// If any config blocks the tool, it's blocked
|
|
201
|
-
if (allowance === "0") {
|
|
202
|
-
merged[toolId] = "0";
|
|
203
|
-
}
|
|
204
|
-
else if (!merged[toolId]) {
|
|
205
|
-
// If not already set, use this allowance
|
|
206
|
-
merged[toolId] = allowance;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return merged;
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Merge multiple student accommodation profiles
|
|
214
|
-
*
|
|
215
|
-
* Union of all accommodations.
|
|
216
|
-
*
|
|
217
|
-
* @param profiles Array of student profiles
|
|
218
|
-
* @returns Merged profile
|
|
219
|
-
*/
|
|
220
|
-
mergeStudentProfiles(...profiles) {
|
|
221
|
-
const allAccommodations = new Set();
|
|
222
|
-
for (const profile of profiles) {
|
|
223
|
-
profile.accommodations.forEach((acc) => allAccommodations.add(acc));
|
|
224
|
-
}
|
|
225
|
-
return {
|
|
226
|
-
accommodations: Array.from(allAccommodations),
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
}
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validation primitives shared by every catalog card whose payload references
|
|
3
|
-
* media.
|
|
4
|
-
*
|
|
5
|
-
* Extracted from `sign-language-cards.ts` when `spoken` cards gained a recorded
|
|
6
|
-
* audio form: both card types take authored, wire-facing URLs and hand them to a
|
|
7
|
-
* media element in the learner's browser, so both need the same scheme
|
|
8
|
-
* allow-list and the same "treat as absent, never as partially valid" posture.
|
|
9
|
-
* Two copies of a URL allow-list is one copy that gets a fix and one that does
|
|
10
|
-
* not.
|
|
11
|
-
*
|
|
12
|
-
* Part of PIE Assessment Toolkit.
|
|
13
|
-
*/
|
|
14
|
-
import type { MediaFragmentRange, MediaSource } from "@pie-players/pie-players-shared/types";
|
|
15
|
-
/**
|
|
16
|
-
* The only `MediaAssetRef.version` this build renders.
|
|
17
|
-
*
|
|
18
|
-
* `media-asset-contract.md` requires unknown-version rejection for runtime
|
|
19
|
-
* rendering: bumping to `2` obliges every consumer to accept both for as long as
|
|
20
|
-
* any producer emits `1`, so a card claiming a version this build does not
|
|
21
|
-
* implement must not be rendered on a guess at which fields still mean what they
|
|
22
|
-
* did.
|
|
23
|
-
*/
|
|
24
|
-
export declare const SUPPORTED_MEDIA_ASSET_VERSION = 1;
|
|
25
|
-
/**
|
|
26
|
-
* Whether a media reference claims a version this build cannot render.
|
|
27
|
-
*
|
|
28
|
-
* An absent `version` is accepted rather than rejected — the same posture the
|
|
29
|
-
* rest of this module takes toward absent fields, and the one `media.kind` and
|
|
30
|
-
* `matchesRequestedSignLanguage` already take: only a positive claim of
|
|
31
|
-
* something else is refused. Producers predate the field, and treating its
|
|
32
|
-
* absence as a rejection would drop cards that are otherwise wholly valid.
|
|
33
|
-
*/
|
|
34
|
-
export declare function isUnsupportedMediaAssetVersion(version: unknown): boolean;
|
|
35
|
-
export declare function isSafeMediaSrc(raw: unknown): raw is string;
|
|
36
|
-
export declare function normalizeMediaSources(raw: unknown): MediaSource[];
|
|
37
|
-
export declare function normalizeMediaFragment(raw: unknown): MediaFragmentRange | undefined;
|
|
38
|
-
export declare function trimmedOrUndefined(value: unknown): string | undefined;
|
|
39
|
-
/**
|
|
40
|
-
* Hold a media element to one fragment's range, and return the disposer.
|
|
41
|
-
*
|
|
42
|
-
* The `#t=` URI `applyMediaFragment` writes is a hint browsers honour at neither
|
|
43
|
-
* bound reliably, so every consumer that means "play only this slice" enforces it
|
|
44
|
-
* here instead of reimplementing the pair. Both shipped consumers do —
|
|
45
|
-
* `SignLanguageMediaRegion` pausing at the end, `TTSService.playRecordedAudio`
|
|
46
|
-
* ending the clip so the chunk sequence advances — which is why the end action is
|
|
47
|
-
* the caller's and only the arithmetic is shared.
|
|
48
|
-
*
|
|
49
|
-
* The start seek is forward only: a browser that did honour the URI has already
|
|
50
|
-
* positioned past the start, and seeking back would replay audio the learner has
|
|
51
|
-
* heard. `onReachedEnd` may fire more than once and must tolerate it; polling
|
|
52
|
-
* stops while the element is paused, so an element left sitting past its end bound
|
|
53
|
-
* goes quiet rather than being told repeatedly.
|
|
54
|
-
*/
|
|
55
|
-
export declare function enforceMediaFragment(element: HTMLMediaElement, fragment: MediaFragmentRange | undefined, onReachedEnd: () => void): () => void;
|
|
56
|
-
export declare function applyMediaFragment(src: string, fragment?: MediaFragmentRange): string;
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Validation primitives shared by every catalog card whose payload references
|
|
3
|
-
* media.
|
|
4
|
-
*
|
|
5
|
-
* Extracted from `sign-language-cards.ts` when `spoken` cards gained a recorded
|
|
6
|
-
* audio form: both card types take authored, wire-facing URLs and hand them to a
|
|
7
|
-
* media element in the learner's browser, so both need the same scheme
|
|
8
|
-
* allow-list and the same "treat as absent, never as partially valid" posture.
|
|
9
|
-
* Two copies of a URL allow-list is one copy that gets a fix and one that does
|
|
10
|
-
* not.
|
|
11
|
-
*
|
|
12
|
-
* Part of PIE Assessment Toolkit.
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* Media source URLs are handed to a media element in the learner's browser. Only
|
|
16
|
-
* schemes such an element can actually fetch are allowed; anything else is
|
|
17
|
-
* dropped so an authored `javascript:` / `file:` URL cannot ride into the DOM.
|
|
18
|
-
* Relative and protocol-relative URLs are allowed — host content is commonly
|
|
19
|
-
* served from the same origin as the player.
|
|
20
|
-
*/
|
|
21
|
-
const DISALLOWED_SRC_SCHEME = /^[a-z][a-z0-9+.-]*:/i;
|
|
22
|
-
const ALLOWED_SRC_SCHEMES = new Set(["http:", "https:", "data:", "blob:"]);
|
|
23
|
-
/**
|
|
24
|
-
* The only `MediaAssetRef.version` this build renders.
|
|
25
|
-
*
|
|
26
|
-
* `media-asset-contract.md` requires unknown-version rejection for runtime
|
|
27
|
-
* rendering: bumping to `2` obliges every consumer to accept both for as long as
|
|
28
|
-
* any producer emits `1`, so a card claiming a version this build does not
|
|
29
|
-
* implement must not be rendered on a guess at which fields still mean what they
|
|
30
|
-
* did.
|
|
31
|
-
*/
|
|
32
|
-
export const SUPPORTED_MEDIA_ASSET_VERSION = 1;
|
|
33
|
-
/**
|
|
34
|
-
* Whether a media reference claims a version this build cannot render.
|
|
35
|
-
*
|
|
36
|
-
* An absent `version` is accepted rather than rejected — the same posture the
|
|
37
|
-
* rest of this module takes toward absent fields, and the one `media.kind` and
|
|
38
|
-
* `matchesRequestedSignLanguage` already take: only a positive claim of
|
|
39
|
-
* something else is refused. Producers predate the field, and treating its
|
|
40
|
-
* absence as a rejection would drop cards that are otherwise wholly valid.
|
|
41
|
-
*/
|
|
42
|
-
export function isUnsupportedMediaAssetVersion(version) {
|
|
43
|
-
return version !== undefined && version !== SUPPORTED_MEDIA_ASSET_VERSION;
|
|
44
|
-
}
|
|
45
|
-
export function isSafeMediaSrc(raw) {
|
|
46
|
-
if (typeof raw !== "string")
|
|
47
|
-
return false;
|
|
48
|
-
const src = raw.trim();
|
|
49
|
-
if (!src)
|
|
50
|
-
return false;
|
|
51
|
-
// Relative ("/video.mp4", "video.mp4") and protocol-relative ("//cdn/x.mp4")
|
|
52
|
-
// forms carry no scheme to check and inherit the document's.
|
|
53
|
-
if (src.startsWith("//") || !DISALLOWED_SRC_SCHEME.test(src))
|
|
54
|
-
return true;
|
|
55
|
-
const scheme = src.slice(0, src.indexOf(":") + 1).toLowerCase();
|
|
56
|
-
return ALLOWED_SRC_SCHEMES.has(scheme);
|
|
57
|
-
}
|
|
58
|
-
export function normalizeMediaSources(raw) {
|
|
59
|
-
if (!Array.isArray(raw))
|
|
60
|
-
return [];
|
|
61
|
-
const sources = [];
|
|
62
|
-
for (const entry of raw) {
|
|
63
|
-
if (!entry || typeof entry !== "object")
|
|
64
|
-
continue;
|
|
65
|
-
const candidate = entry;
|
|
66
|
-
if (!isSafeMediaSrc(candidate.src))
|
|
67
|
-
continue;
|
|
68
|
-
const source = { src: candidate.src.trim() };
|
|
69
|
-
if (typeof candidate.type === "string" && candidate.type.trim()) {
|
|
70
|
-
source.type = candidate.type.trim();
|
|
71
|
-
}
|
|
72
|
-
if (Number.isFinite(candidate.width))
|
|
73
|
-
source.width = candidate.width;
|
|
74
|
-
if (Number.isFinite(candidate.height))
|
|
75
|
-
source.height = candidate.height;
|
|
76
|
-
// Deduplicated by `src`, because the signing region renders `<source>`
|
|
77
|
-
// elements in an `{#each}` keyed on exactly that: an authored card listing
|
|
78
|
-
// one URL twice — the same file under two MIME types is the plausible way —
|
|
79
|
-
// would otherwise throw Svelte's duplicate-key error and take the whole
|
|
80
|
-
// region down rather than degrade. The first entry wins, so authored order
|
|
81
|
-
// still decides which encoding the browser is offered first.
|
|
82
|
-
if (sources.some((existing) => existing.src === source.src))
|
|
83
|
-
continue;
|
|
84
|
-
sources.push(source);
|
|
85
|
-
}
|
|
86
|
-
return sources;
|
|
87
|
-
}
|
|
88
|
-
export function normalizeMediaFragment(raw) {
|
|
89
|
-
if (!raw || typeof raw !== "object")
|
|
90
|
-
return undefined;
|
|
91
|
-
const candidate = raw;
|
|
92
|
-
const start = Number(candidate.startSeconds);
|
|
93
|
-
if (!Number.isFinite(start) || start < 0)
|
|
94
|
-
return undefined;
|
|
95
|
-
const end = Number(candidate.endSeconds);
|
|
96
|
-
// An end at or before the start would produce a zero/negative slice; treat
|
|
97
|
-
// it as "no end" rather than a range that can never play.
|
|
98
|
-
if (!Number.isFinite(end) || end <= start)
|
|
99
|
-
return { startSeconds: start };
|
|
100
|
-
return { startSeconds: start, endSeconds: end };
|
|
101
|
-
}
|
|
102
|
-
export function trimmedOrUndefined(value) {
|
|
103
|
-
if (typeof value !== "string")
|
|
104
|
-
return undefined;
|
|
105
|
-
const trimmed = value.trim();
|
|
106
|
-
return trimmed || undefined;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Apply a fragment range to a source URL as a Media Fragments URI, so one
|
|
110
|
-
* recording can serve several content nodes.
|
|
111
|
-
*
|
|
112
|
-
* The URI is a hint only: browsers honour both bounds inconsistently, so the
|
|
113
|
-
* caller enforces the range itself — seek forward to the start once metadata is
|
|
114
|
-
* available, and stop at the end. `SignLanguageMediaRegion` and
|
|
115
|
-
* `TTSService.playRecordedAudio` are the two shipped consumers that do so.
|
|
116
|
-
*/
|
|
117
|
-
/**
|
|
118
|
-
* How often the end bound is re-checked while a slice is playing. `timeupdate`
|
|
119
|
-
* alone fires about four times a second, which is loose enough to leak a sliver
|
|
120
|
-
* of the next node's recording.
|
|
121
|
-
*/
|
|
122
|
-
const END_CHECK_INTERVAL_MS = 100;
|
|
123
|
-
/** `HTMLMediaElement.HAVE_METADATA`, which not every DOM implementation exposes. */
|
|
124
|
-
const HAVE_METADATA = 1;
|
|
125
|
-
/**
|
|
126
|
-
* Hold a media element to one fragment's range, and return the disposer.
|
|
127
|
-
*
|
|
128
|
-
* The `#t=` URI `applyMediaFragment` writes is a hint browsers honour at neither
|
|
129
|
-
* bound reliably, so every consumer that means "play only this slice" enforces it
|
|
130
|
-
* here instead of reimplementing the pair. Both shipped consumers do —
|
|
131
|
-
* `SignLanguageMediaRegion` pausing at the end, `TTSService.playRecordedAudio`
|
|
132
|
-
* ending the clip so the chunk sequence advances — which is why the end action is
|
|
133
|
-
* the caller's and only the arithmetic is shared.
|
|
134
|
-
*
|
|
135
|
-
* The start seek is forward only: a browser that did honour the URI has already
|
|
136
|
-
* positioned past the start, and seeking back would replay audio the learner has
|
|
137
|
-
* heard. `onReachedEnd` may fire more than once and must tolerate it; polling
|
|
138
|
-
* stops while the element is paused, so an element left sitting past its end bound
|
|
139
|
-
* goes quiet rather than being told repeatedly.
|
|
140
|
-
*/
|
|
141
|
-
export function enforceMediaFragment(element, fragment, onReachedEnd) {
|
|
142
|
-
if (!fragment)
|
|
143
|
-
return () => { };
|
|
144
|
-
const { startSeconds, endSeconds } = fragment;
|
|
145
|
-
let seekToStart;
|
|
146
|
-
if (startSeconds > 0) {
|
|
147
|
-
seekToStart = () => {
|
|
148
|
-
if (element.currentTime < startSeconds) {
|
|
149
|
-
element.currentTime = startSeconds;
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
// Seeking before metadata is available only sets the default start
|
|
153
|
-
// position, so wait for it unless it already landed.
|
|
154
|
-
if (element.readyState >= HAVE_METADATA)
|
|
155
|
-
seekToStart();
|
|
156
|
-
else
|
|
157
|
-
element.addEventListener("loadedmetadata", seekToStart);
|
|
158
|
-
}
|
|
159
|
-
let checkEnd;
|
|
160
|
-
let endPoll;
|
|
161
|
-
if (endSeconds !== undefined) {
|
|
162
|
-
checkEnd = () => {
|
|
163
|
-
if (element.currentTime >= endSeconds)
|
|
164
|
-
onReachedEnd();
|
|
165
|
-
};
|
|
166
|
-
element.addEventListener("timeupdate", checkEnd);
|
|
167
|
-
endPoll = setInterval(() => {
|
|
168
|
-
if (!element.paused)
|
|
169
|
-
checkEnd?.();
|
|
170
|
-
}, END_CHECK_INTERVAL_MS);
|
|
171
|
-
}
|
|
172
|
-
return () => {
|
|
173
|
-
if (seekToStart)
|
|
174
|
-
element.removeEventListener("loadedmetadata", seekToStart);
|
|
175
|
-
if (checkEnd)
|
|
176
|
-
element.removeEventListener("timeupdate", checkEnd);
|
|
177
|
-
if (endPoll !== undefined)
|
|
178
|
-
clearInterval(endPoll);
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
export function applyMediaFragment(src, fragment) {
|
|
182
|
-
if (!fragment)
|
|
183
|
-
return src;
|
|
184
|
-
// Never stack a second fragment onto a URL that already carries one — the
|
|
185
|
-
// authored value wins.
|
|
186
|
-
if (src.includes("#"))
|
|
187
|
-
return src;
|
|
188
|
-
const end = fragment.endSeconds !== undefined ? `,${fragment.endSeconds}` : "";
|
|
189
|
-
return `${src}#t=${fragment.startSeconds}${end}`;
|
|
190
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tool Registry Factory
|
|
3
|
-
*
|
|
4
|
-
* Creates an empty `ToolRegistry` and installs whatever component overrides and
|
|
5
|
-
* lazy module loaders the caller supplies.
|
|
6
|
-
*
|
|
7
|
-
* It registers nothing by itself, deliberately. Eleven concrete registrations
|
|
8
|
-
* used to live in this package, which made the policy and registry core know
|
|
9
|
-
* every capability by name and left a host unable to contribute one without a PR
|
|
10
|
-
* against this package. The packaged set now lives in the composition layer:
|
|
11
|
-
* `createPackagedToolRegistry` from `@pie-players/pie-default-tool-loaders`.
|
|
12
|
-
*/
|
|
13
|
-
import type { ToolRegistration, ToolModuleLoader } from "./ToolRegistry.js";
|
|
14
|
-
import { ToolRegistry } from "./ToolRegistry.js";
|
|
15
|
-
import type { ToolComponentFactory, ToolComponentFactoryMap, ToolTagMap } from "../tools/tool-tag-map.js";
|
|
16
|
-
import { DEFAULT_TOOL_PLACEMENT } from "./tool-config-defaults.js";
|
|
17
|
-
export interface DefaultToolRegistryOptions {
|
|
18
|
-
/**
|
|
19
|
-
* Registrations to register, keyed by toolId. Nothing is registered when this
|
|
20
|
-
* is absent.
|
|
21
|
-
*/
|
|
22
|
-
registrations?: Partial<Record<string, ToolRegistration>>;
|
|
23
|
-
/**
|
|
24
|
-
* Element tag mapping for the registrations being installed. There is no
|
|
25
|
-
* built-in default: a tag map names capabilities, so it belongs to whoever
|
|
26
|
-
* decides which capabilities exist.
|
|
27
|
-
*/
|
|
28
|
-
toolTagMap?: Partial<ToolTagMap>;
|
|
29
|
-
/**
|
|
30
|
-
* Override component creation globally (all tools) or per tool.
|
|
31
|
-
*/
|
|
32
|
-
toolComponentFactory?: ToolComponentFactory;
|
|
33
|
-
toolComponentFactories?: Partial<ToolComponentFactoryMap>;
|
|
34
|
-
/**
|
|
35
|
-
* Optional lazy module loaders keyed by toolId.
|
|
36
|
-
*/
|
|
37
|
-
toolModuleLoaders?: Partial<Record<string, ToolModuleLoader>>;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Create a tool registry.
|
|
41
|
-
*
|
|
42
|
-
* @returns an empty `ToolRegistry` unless `registrations` is supplied
|
|
43
|
-
*/
|
|
44
|
-
export declare function createDefaultToolRegistry(options?: DefaultToolRegistryOptions): ToolRegistry;
|
|
45
|
-
/**
|
|
46
|
-
* Placement configuration with every level empty.
|
|
47
|
-
*
|
|
48
|
-
* The only placement default this package can hold: a populated one would name
|
|
49
|
-
* capabilities. `PACKAGED_TOOL_PLACEMENT` and
|
|
50
|
-
* `SECTION_PLAYER_PREFERRED_TOOL_PLACEMENT` are in
|
|
51
|
-
* `@pie-players/pie-default-tool-loaders`.
|
|
52
|
-
*/
|
|
53
|
-
export { DEFAULT_TOOL_PLACEMENT };
|