@pie-players/pie-assessment-toolkit 0.3.44 → 0.3.45
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 +54 -69
- package/dist/components/ItemToolBar.custom-element.js +479 -324
- package/dist/components/PieAssessmentToolkit.custom-element.js +19498 -3590
- package/dist/components/SectionToolBar.custom-element.js +761 -507
- package/dist/index.d.ts +3 -6
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/policy/core/ToolPolicyEngine.d.ts +1 -2
- package/dist/policy/core/ToolPolicyEngine.js +1 -2
- package/dist/policy/core/ToolPolicyEngine.js.map +1 -1
- package/dist/policy/core/pnp-policy-inputs.d.ts +2 -3
- package/dist/policy/core/pnp-policy-inputs.js +2 -3
- package/dist/policy/core/pnp-policy-inputs.js.map +1 -1
- package/dist/policy/core/provenance.d.ts +7 -34
- package/dist/policy/core/provenance.js +4 -21
- package/dist/policy/core/provenance.js.map +1 -1
- package/dist/policy/sources/PnpPolicySource.d.ts +6 -19
- package/dist/policy/sources/PnpPolicySource.js +6 -19
- package/dist/policy/sources/PnpPolicySource.js.map +1 -1
- package/dist/runtime/SectionRuntimeEngine.d.ts +13 -28
- package/dist/runtime/SectionRuntimeEngine.js +13 -27
- package/dist/runtime/SectionRuntimeEngine.js.map +1 -1
- package/dist/runtime/adapter/SectionEngineAdapter.d.ts +2 -2
- package/dist/runtime/adapter/SectionEngineAdapter.js +2 -2
- package/dist/runtime/adapter/SectionEngineAdapter.js.map +1 -1
- package/dist/runtime/adapter/coordinator-bridge.d.ts +1 -1
- package/dist/runtime/adapter/coordinator-bridge.js +1 -1
- package/dist/runtime/adapter/coordinator-bridge.js.map +1 -1
- package/dist/runtime/adapter/dom-event-bridge.d.ts +1 -1
- package/dist/runtime/adapter/dom-event-bridge.js +1 -1
- package/dist/runtime/adapter/dom-event-bridge.js.map +1 -1
- package/dist/runtime/catalog-registration.d.ts +12 -0
- package/dist/runtime/catalog-registration.js +54 -0
- package/dist/runtime/catalog-registration.js.map +1 -0
- package/dist/runtime/core/engine-output.d.ts +2 -2
- package/dist/runtime/core/engine-output.js +2 -2
- package/dist/runtime/core/engine-output.js.map +1 -1
- package/dist/runtime/core/engine-readiness.d.ts +2 -2
- package/dist/runtime/core/engine-readiness.js.map +1 -1
- package/dist/runtime/core/engine-resolver.d.ts +13 -41
- package/dist/runtime/core/engine-resolver.js +19 -76
- package/dist/runtime/core/engine-resolver.js.map +1 -1
- package/dist/runtime/core/engine-transition.d.ts +1 -1
- package/dist/runtime/core/engine-transition.js +1 -1
- package/dist/runtime/core/engine-transition.js.map +1 -1
- package/dist/runtime/engine.d.ts +1 -1
- package/dist/runtime/engine.js.map +1 -1
- package/dist/runtime/section-runtime-engine-host-context.d.ts +22 -15
- package/dist/runtime/section-runtime-engine-host-context.js +10 -10
- package/dist/runtime/section-runtime-engine-host-context.js.map +1 -1
- package/dist/runtime/stage-emit-gate.d.ts +4 -4
- package/dist/runtime/stage-emit-gate.js +3 -3
- package/dist/runtime/stage-emit-gate.js.map +1 -1
- package/dist/services/AccessibilityCatalogResolver.d.ts +23 -0
- package/dist/services/AccessibilityCatalogResolver.js +200 -28
- package/dist/services/AccessibilityCatalogResolver.js.map +1 -1
- package/dist/services/HighlightCoordinator.d.ts +25 -0
- package/dist/services/HighlightCoordinator.js +125 -0
- package/dist/services/HighlightCoordinator.js.map +1 -1
- package/dist/services/SSMLExtractor.d.ts +16 -1
- package/dist/services/SSMLExtractor.js +86 -7
- package/dist/services/SSMLExtractor.js.map +1 -1
- package/dist/services/TTSService.d.ts +30 -4
- package/dist/services/TTSService.js +652 -159
- package/dist/services/TTSService.js.map +1 -1
- package/dist/services/ToolRegistry.d.ts +2 -2
- package/dist/services/ToolRegistry.js +1 -1
- package/dist/services/ToolRegistry.js.map +1 -1
- package/dist/services/ToolkitCoordinator.d.ts +16 -73
- package/dist/services/ToolkitCoordinator.js +34 -113
- package/dist/services/ToolkitCoordinator.js.map +1 -1
- package/dist/services/interfaces.d.ts +20 -2
- package/dist/services/interfaces.js.map +1 -1
- package/dist/services/tool-config-validation.d.ts +1 -1
- package/dist/services/tool-config-validation.js +7 -7
- package/dist/services/tool-config-validation.js.map +1 -1
- package/dist/services/tool-instance-id.js +1 -1
- package/dist/services/tool-instance-id.js.map +1 -1
- package/dist/services/tool-providers/TTSToolProvider.js +3 -3
- package/dist/services/tool-providers/TTSToolProvider.js.map +1 -1
- package/dist/services/tools-config-normalizer.d.ts +13 -2
- package/dist/services/tools-config-normalizer.js +3 -8
- package/dist/services/tools-config-normalizer.js.map +1 -1
- package/dist/services/tts/browser-provider.js +2 -0
- package/dist/services/tts/browser-provider.js.map +1 -1
- package/dist/services/tts/catalog-span-alignment.d.ts +39 -0
- package/dist/services/tts/catalog-span-alignment.js +300 -0
- package/dist/services/tts/catalog-span-alignment.js.map +1 -0
- package/dist/services/tts/generated-speech/assemble-plan.d.ts +33 -0
- package/dist/services/tts/generated-speech/assemble-plan.js +111 -0
- package/dist/services/tts/generated-speech/assemble-plan.js.map +1 -0
- package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +25 -0
- package/dist/services/tts/generated-speech/dom/build-dom-plan.js +66 -0
- package/dist/services/tts/generated-speech/dom/build-dom-plan.js.map +1 -0
- package/dist/services/tts/generated-speech/dom/to-chunk-inputs.d.ts +19 -0
- package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js +71 -0
- package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js.map +1 -0
- package/dist/services/tts/generated-speech/dom/types.d.ts +37 -0
- package/dist/services/tts/generated-speech/dom/types.js +2 -0
- package/dist/services/tts/generated-speech/dom/types.js.map +1 -0
- package/dist/services/tts/generated-speech/index.d.ts +17 -0
- package/dist/services/tts/generated-speech/index.js +17 -0
- package/dist/services/tts/generated-speech/index.js.map +1 -0
- package/dist/services/tts/generated-speech/math-speech-cache.d.ts +15 -0
- package/dist/services/tts/generated-speech/math-speech-cache.js +78 -0
- package/dist/services/tts/generated-speech/math-speech-cache.js.map +1 -0
- package/dist/services/tts/generated-speech/types.d.ts +104 -0
- package/dist/services/tts/generated-speech/types.js +18 -0
- package/dist/services/tts/generated-speech/types.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/boundary-normalizer.d.ts +2 -0
- package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +106 -0
- package/dist/services/tts/highlight-pipeline/boundary-normalizer.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.d.ts +6 -0
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +217 -0
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/highlight-policy.d.ts +2 -0
- package/dist/services/tts/highlight-pipeline/highlight-policy.js +32 -0
- package/dist/services/tts/highlight-pipeline/highlight-policy.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/index.d.ts +9 -0
- package/dist/services/tts/highlight-pipeline/index.js +9 -0
- package/dist/services/tts/highlight-pipeline/index.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/math-semantic-resolver.d.ts +3 -0
- package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js +32 -0
- package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.d.ts +29 -0
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +44 -0
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.d.ts +2 -0
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +20 -0
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/readable-region.d.ts +1 -0
- package/dist/services/tts/highlight-pipeline/readable-region.js +19 -0
- package/dist/services/tts/highlight-pipeline/readable-region.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.d.ts +6 -0
- package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js +198 -0
- package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/types.d.ts +72 -0
- package/dist/services/tts/highlight-pipeline/types.js +2 -0
- package/dist/services/tts/highlight-pipeline/types.js.map +1 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +2 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.js +22 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.js.map +1 -0
- package/dist/services/tts/math-alignment/index.d.ts +30 -0
- package/dist/services/tts/math-alignment/index.js +66 -0
- package/dist/services/tts/math-alignment/index.js.map +1 -0
- package/dist/services/tts/math-alignment/mathml-tokenizer.d.ts +2 -0
- package/dist/services/tts/math-alignment/mathml-tokenizer.js +317 -0
- package/dist/services/tts/math-alignment/mathml-tokenizer.js.map +1 -0
- package/dist/services/tts/math-alignment/range-resolver.d.ts +10 -0
- package/dist/services/tts/math-alignment/range-resolver.js +13 -0
- package/dist/services/tts/math-alignment/range-resolver.js.map +1 -0
- package/dist/services/tts/math-alignment/sequence-aligner.d.ts +7 -0
- package/dist/services/tts/math-alignment/sequence-aligner.js +119 -0
- package/dist/services/tts/math-alignment/sequence-aligner.js.map +1 -0
- package/dist/services/tts/math-alignment/speech-tokenizer.d.ts +31 -0
- package/dist/services/tts/math-alignment/speech-tokenizer.js +143 -0
- package/dist/services/tts/math-alignment/speech-tokenizer.js.map +1 -0
- package/dist/services/tts/math-alignment/types.d.ts +58 -0
- package/dist/services/tts/math-alignment/types.js +2 -0
- package/dist/services/tts/math-alignment/types.js.map +1 -0
- package/dist/services/tts/math-aware-text-processing.d.ts +18 -0
- package/dist/services/tts/math-aware-text-processing.js +348 -0
- package/dist/services/tts/math-aware-text-processing.js.map +1 -0
- package/dist/services/tts/math-speech.d.ts +44 -0
- package/dist/services/tts/math-speech.js +147 -0
- package/dist/services/tts/math-speech.js.map +1 -0
- package/dist/services/tts/mathml-sanitization.d.ts +1 -0
- package/dist/services/tts/mathml-sanitization.js +170 -0
- package/dist/services/tts/mathml-sanitization.js.map +1 -0
- package/dist/services/tts/ssml/spoken-text.d.ts +33 -0
- package/dist/services/tts/ssml/spoken-text.js +159 -0
- package/dist/services/tts/ssml/spoken-text.js.map +1 -0
- package/dist/services/tts/text-processing.d.ts +3 -0
- package/dist/services/tts/text-processing.js +30 -4
- package/dist/services/tts/text-processing.js.map +1 -1
- package/dist/services/tts-runtime-config.d.ts +17 -1
- package/dist/services/tts-runtime-config.js +11 -1
- package/dist/services/tts-runtime-config.js.map +1 -1
- package/dist/tools/registrations/accessibility-tools.js +0 -1
- package/dist/tools/registrations/accessibility-tools.js.map +1 -1
- package/dist/tools/registrations/calculator.js +41 -2
- package/dist/tools/registrations/calculator.js.map +1 -1
- package/dist/tools/tool-tag-map.js +0 -1
- package/dist/tools/tool-tag-map.js.map +1 -1
- package/package.json +8 -11
- package/dist/services/PNPToolResolver.d.ts +0 -280
- package/dist/services/PNPToolResolver.js +0 -514
- package/dist/services/PNPToolResolver.js.map +0 -1
- package/dist/services/pnp-provenance.d.ts +0 -166
- package/dist/services/pnp-provenance.js +0 -231
- package/dist/services/pnp-provenance.js.map +0 -1
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PNP Resolution Provenance Tracking
|
|
3
|
-
*
|
|
4
|
-
* Provides detailed tracking of how PNP profiles are resolved, including:
|
|
5
|
-
* - Decision steps at each precedence level
|
|
6
|
-
* - Sources of configuration (district, assessment, item, student)
|
|
7
|
-
* - Reasons for tool enablement/blocking
|
|
8
|
-
* - Merge operations and overrides
|
|
9
|
-
*
|
|
10
|
-
* This enables:
|
|
11
|
-
* - Visual explanation components for users
|
|
12
|
-
* - Debugging tool resolution issues
|
|
13
|
-
* - Compliance auditing and reporting
|
|
14
|
-
* - Developer troubleshooting
|
|
15
|
-
*
|
|
16
|
-
* Part of PIE Assessment Toolkit.
|
|
17
|
-
*/
|
|
18
|
-
/**
|
|
19
|
-
* Decision made during PNP resolution
|
|
20
|
-
*/
|
|
21
|
-
export interface ResolutionDecision {
|
|
22
|
-
/** Unique step identifier */
|
|
23
|
-
step: number;
|
|
24
|
-
/** Precedence level (1-6, lower = higher priority) */
|
|
25
|
-
precedence: 1 | 2 | 3 | 4 | 5 | 6;
|
|
26
|
-
/** Human-readable rule name */
|
|
27
|
-
rule: "district-block" | "test-admin-override" | "item-restriction" | "item-requirement" | "district-requirement" | "pnp-support" | "pnp-prohibited" | "system-default";
|
|
28
|
-
/** Tool or feature ID being resolved */
|
|
29
|
-
featureId: string;
|
|
30
|
-
/** Decision outcome */
|
|
31
|
-
action: "block" | "enable" | "skip";
|
|
32
|
-
/** Where the decision came from */
|
|
33
|
-
source: {
|
|
34
|
-
type: "organization" | "assessment" | "section" | "item" | "student" | "system";
|
|
35
|
-
id?: string;
|
|
36
|
-
name?: string;
|
|
37
|
-
};
|
|
38
|
-
/** Why this decision was made */
|
|
39
|
-
reason: string;
|
|
40
|
-
/** Configuration value that triggered this decision */
|
|
41
|
-
value?: any;
|
|
42
|
-
/** Timestamp of decision */
|
|
43
|
-
timestamp: Date;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Complete resolution trail for a single feature
|
|
47
|
-
*/
|
|
48
|
-
export interface FeatureResolutionTrail {
|
|
49
|
-
/** Feature ID (QTI 3.0 or tool ID) */
|
|
50
|
-
featureId: string;
|
|
51
|
-
/** Final decision */
|
|
52
|
-
finalState: "enabled" | "blocked" | "not-configured";
|
|
53
|
-
/** Winning decision (highest precedence) */
|
|
54
|
-
winningDecision?: ResolutionDecision;
|
|
55
|
-
/** All decisions considered (in precedence order) */
|
|
56
|
-
allDecisions: ResolutionDecision[];
|
|
57
|
-
/** Human-readable explanation */
|
|
58
|
-
explanation: string;
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Complete provenance for an entire PNP resolution
|
|
62
|
-
*/
|
|
63
|
-
export interface PnpResolutionProvenance {
|
|
64
|
-
/** Session or resolution context ID */
|
|
65
|
-
contextId: string;
|
|
66
|
-
/** When resolution occurred */
|
|
67
|
-
resolvedAt: Date;
|
|
68
|
-
/** All configuration sources consulted */
|
|
69
|
-
sources: {
|
|
70
|
-
organization?: {
|
|
71
|
-
id: string;
|
|
72
|
-
name: string;
|
|
73
|
-
pnpDefaults?: any;
|
|
74
|
-
};
|
|
75
|
-
assessment?: {
|
|
76
|
-
id: string;
|
|
77
|
-
name: string;
|
|
78
|
-
settings?: any;
|
|
79
|
-
};
|
|
80
|
-
section?: {
|
|
81
|
-
id: string;
|
|
82
|
-
title?: string;
|
|
83
|
-
settings?: any;
|
|
84
|
-
};
|
|
85
|
-
item?: {
|
|
86
|
-
id: string;
|
|
87
|
-
name?: string;
|
|
88
|
-
settings?: any;
|
|
89
|
-
};
|
|
90
|
-
student?: {
|
|
91
|
-
id: string;
|
|
92
|
-
pnpProfile?: any;
|
|
93
|
-
};
|
|
94
|
-
};
|
|
95
|
-
/** Resolution trail for each feature */
|
|
96
|
-
features: Map<string, FeatureResolutionTrail>;
|
|
97
|
-
/** All decisions in chronological order */
|
|
98
|
-
decisionLog: ResolutionDecision[];
|
|
99
|
-
/** Summary statistics */
|
|
100
|
-
summary: {
|
|
101
|
-
totalFeatures: number;
|
|
102
|
-
enabled: number;
|
|
103
|
-
blocked: number;
|
|
104
|
-
notConfigured: number;
|
|
105
|
-
bySource: Record<string, number>;
|
|
106
|
-
byRule: Record<string, number>;
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Provenance builder for tracking PNP resolution
|
|
111
|
-
*/
|
|
112
|
-
export declare class PnpProvenanceBuilder {
|
|
113
|
-
private provenance;
|
|
114
|
-
private stepCounter;
|
|
115
|
-
constructor(contextId: string);
|
|
116
|
-
/**
|
|
117
|
-
* Add a configuration source
|
|
118
|
-
*/
|
|
119
|
-
addSource(type: "organization" | "assessment" | "section" | "item" | "student", data: {
|
|
120
|
-
id: string;
|
|
121
|
-
name?: string;
|
|
122
|
-
config?: any;
|
|
123
|
-
}): this;
|
|
124
|
-
/**
|
|
125
|
-
* Record a decision during resolution
|
|
126
|
-
*/
|
|
127
|
-
addDecision(params: {
|
|
128
|
-
precedence: 1 | 2 | 3 | 4 | 5 | 6;
|
|
129
|
-
rule: ResolutionDecision["rule"];
|
|
130
|
-
featureId: string;
|
|
131
|
-
action: "block" | "enable" | "skip";
|
|
132
|
-
sourceType: ResolutionDecision["source"]["type"];
|
|
133
|
-
sourceId?: string;
|
|
134
|
-
sourceName?: string;
|
|
135
|
-
reason: string;
|
|
136
|
-
value?: any;
|
|
137
|
-
}): this;
|
|
138
|
-
/**
|
|
139
|
-
* Build final provenance with explanations
|
|
140
|
-
*/
|
|
141
|
-
build(): PnpResolutionProvenance;
|
|
142
|
-
/**
|
|
143
|
-
* Generate human-readable explanation for a feature resolution
|
|
144
|
-
*/
|
|
145
|
-
private generateExplanation;
|
|
146
|
-
/**
|
|
147
|
-
* Get human-readable rule name
|
|
148
|
-
*/
|
|
149
|
-
private getRuleName;
|
|
150
|
-
/**
|
|
151
|
-
* Get current provenance (without explanations)
|
|
152
|
-
*/
|
|
153
|
-
getCurrent(): PnpResolutionProvenance;
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Format provenance as markdown for display
|
|
157
|
-
*/
|
|
158
|
-
export declare function formatProvenanceAsMarkdown(provenance: PnpResolutionProvenance): string;
|
|
159
|
-
/**
|
|
160
|
-
* Format provenance as JSON (for APIs)
|
|
161
|
-
*/
|
|
162
|
-
export declare function formatProvenanceAsJSON(provenance: PnpResolutionProvenance): string;
|
|
163
|
-
/**
|
|
164
|
-
* Get simple explanation for a specific feature
|
|
165
|
-
*/
|
|
166
|
-
export declare function getFeatureExplanation(provenance: PnpResolutionProvenance, featureId: string): string | null;
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PNP Resolution Provenance Tracking
|
|
3
|
-
*
|
|
4
|
-
* Provides detailed tracking of how PNP profiles are resolved, including:
|
|
5
|
-
* - Decision steps at each precedence level
|
|
6
|
-
* - Sources of configuration (district, assessment, item, student)
|
|
7
|
-
* - Reasons for tool enablement/blocking
|
|
8
|
-
* - Merge operations and overrides
|
|
9
|
-
*
|
|
10
|
-
* This enables:
|
|
11
|
-
* - Visual explanation components for users
|
|
12
|
-
* - Debugging tool resolution issues
|
|
13
|
-
* - Compliance auditing and reporting
|
|
14
|
-
* - Developer troubleshooting
|
|
15
|
-
*
|
|
16
|
-
* Part of PIE Assessment Toolkit.
|
|
17
|
-
*/
|
|
18
|
-
/**
|
|
19
|
-
* Provenance builder for tracking PNP resolution
|
|
20
|
-
*/
|
|
21
|
-
export class PnpProvenanceBuilder {
|
|
22
|
-
provenance;
|
|
23
|
-
stepCounter = 0;
|
|
24
|
-
constructor(contextId) {
|
|
25
|
-
this.provenance = {
|
|
26
|
-
contextId,
|
|
27
|
-
resolvedAt: new Date(),
|
|
28
|
-
sources: {},
|
|
29
|
-
features: new Map(),
|
|
30
|
-
decisionLog: [],
|
|
31
|
-
summary: {
|
|
32
|
-
totalFeatures: 0,
|
|
33
|
-
enabled: 0,
|
|
34
|
-
blocked: 0,
|
|
35
|
-
notConfigured: 0,
|
|
36
|
-
bySource: {},
|
|
37
|
-
byRule: {},
|
|
38
|
-
},
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Add a configuration source
|
|
43
|
-
*/
|
|
44
|
-
addSource(type, data) {
|
|
45
|
-
this.provenance.sources[type] = {
|
|
46
|
-
id: data.id,
|
|
47
|
-
name: data.name || data.id,
|
|
48
|
-
...(type === "organization" && { pnpDefaults: data.config }),
|
|
49
|
-
...(type === "assessment" && { settings: data.config }),
|
|
50
|
-
...(type === "section" && { settings: data.config }),
|
|
51
|
-
...(type === "item" && { settings: data.config }),
|
|
52
|
-
...(type === "student" && { pnpProfile: data.config }),
|
|
53
|
-
};
|
|
54
|
-
return this;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Record a decision during resolution
|
|
58
|
-
*/
|
|
59
|
-
addDecision(params) {
|
|
60
|
-
const decision = {
|
|
61
|
-
step: ++this.stepCounter,
|
|
62
|
-
precedence: params.precedence,
|
|
63
|
-
rule: params.rule,
|
|
64
|
-
featureId: params.featureId,
|
|
65
|
-
action: params.action,
|
|
66
|
-
source: {
|
|
67
|
-
type: params.sourceType,
|
|
68
|
-
id: params.sourceId,
|
|
69
|
-
name: params.sourceName,
|
|
70
|
-
},
|
|
71
|
-
reason: params.reason,
|
|
72
|
-
value: params.value,
|
|
73
|
-
timestamp: new Date(),
|
|
74
|
-
};
|
|
75
|
-
// Add to decision log
|
|
76
|
-
this.provenance.decisionLog.push(decision);
|
|
77
|
-
// Update feature trail
|
|
78
|
-
if (!this.provenance.features.has(params.featureId)) {
|
|
79
|
-
this.provenance.features.set(params.featureId, {
|
|
80
|
-
featureId: params.featureId,
|
|
81
|
-
finalState: "not-configured",
|
|
82
|
-
allDecisions: [],
|
|
83
|
-
explanation: "",
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
const trail = this.provenance.features.get(params.featureId);
|
|
87
|
-
trail.allDecisions.push(decision);
|
|
88
|
-
// Update winning decision if this has higher precedence
|
|
89
|
-
if (params.action !== "skip" &&
|
|
90
|
-
(!trail.winningDecision ||
|
|
91
|
-
decision.precedence < trail.winningDecision.precedence)) {
|
|
92
|
-
trail.winningDecision = decision;
|
|
93
|
-
trail.finalState = decision.action === "enable" ? "enabled" : "blocked";
|
|
94
|
-
}
|
|
95
|
-
// Update summary
|
|
96
|
-
this.provenance.summary.bySource[params.sourceType] =
|
|
97
|
-
(this.provenance.summary.bySource[params.sourceType] || 0) + 1;
|
|
98
|
-
this.provenance.summary.byRule[params.rule] =
|
|
99
|
-
(this.provenance.summary.byRule[params.rule] || 0) + 1;
|
|
100
|
-
return this;
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Build final provenance with explanations
|
|
104
|
-
*/
|
|
105
|
-
build() {
|
|
106
|
-
// Generate explanations for each feature
|
|
107
|
-
for (const [featureId, trail] of this.provenance.features.entries()) {
|
|
108
|
-
trail.explanation = this.generateExplanation(trail);
|
|
109
|
-
}
|
|
110
|
-
// Update summary counts
|
|
111
|
-
for (const trail of this.provenance.features.values()) {
|
|
112
|
-
this.provenance.summary.totalFeatures++;
|
|
113
|
-
if (trail.finalState === "enabled") {
|
|
114
|
-
this.provenance.summary.enabled++;
|
|
115
|
-
}
|
|
116
|
-
else if (trail.finalState === "blocked") {
|
|
117
|
-
this.provenance.summary.blocked++;
|
|
118
|
-
}
|
|
119
|
-
else {
|
|
120
|
-
this.provenance.summary.notConfigured++;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return this.provenance;
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Generate human-readable explanation for a feature resolution
|
|
127
|
-
*/
|
|
128
|
-
generateExplanation(trail) {
|
|
129
|
-
if (!trail.winningDecision) {
|
|
130
|
-
return `Feature "${trail.featureId}" was not configured at any level.`;
|
|
131
|
-
}
|
|
132
|
-
const decision = trail.winningDecision;
|
|
133
|
-
const source = decision.source;
|
|
134
|
-
const sourceName = source.name || source.type;
|
|
135
|
-
let explanation = `Feature "${trail.featureId}" is ${trail.finalState}.\n\n`;
|
|
136
|
-
// Explain winning decision
|
|
137
|
-
explanation += `**Primary Reason** (${this.getRuleName(decision.rule)}):\n`;
|
|
138
|
-
explanation += `${decision.reason}\n`;
|
|
139
|
-
explanation += `Source: ${sourceName} (${source.type})\n\n`;
|
|
140
|
-
// Show overridden decisions if any
|
|
141
|
-
const overridden = trail.allDecisions.filter((d) => d.step !== decision.step &&
|
|
142
|
-
d.action !== "skip" &&
|
|
143
|
-
d.precedence > decision.precedence);
|
|
144
|
-
if (overridden.length > 0) {
|
|
145
|
-
explanation += `**Overridden Rules**:\n`;
|
|
146
|
-
overridden.forEach((d) => {
|
|
147
|
-
explanation += `- ${this.getRuleName(d.rule)}: ${d.reason} (overridden by higher precedence)\n`;
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
return explanation;
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Get human-readable rule name
|
|
154
|
-
*/
|
|
155
|
-
getRuleName(rule) {
|
|
156
|
-
const names = {
|
|
157
|
-
"district-block": "District Block",
|
|
158
|
-
"test-admin-override": "Test Administrator Override",
|
|
159
|
-
"item-restriction": "Item Restriction",
|
|
160
|
-
"item-requirement": "Item Requirement",
|
|
161
|
-
"district-requirement": "District Requirement",
|
|
162
|
-
"pnp-support": "Student PNP Support",
|
|
163
|
-
"pnp-prohibited": "Student PNP Prohibition",
|
|
164
|
-
"system-default": "System Default",
|
|
165
|
-
};
|
|
166
|
-
return names[rule];
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Get current provenance (without explanations)
|
|
170
|
-
*/
|
|
171
|
-
getCurrent() {
|
|
172
|
-
return this.provenance;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Format provenance as markdown for display
|
|
177
|
-
*/
|
|
178
|
-
export function formatProvenanceAsMarkdown(provenance) {
|
|
179
|
-
let md = `# PNP Resolution Report\n\n`;
|
|
180
|
-
md += `**Context**: ${provenance.contextId}\n`;
|
|
181
|
-
md += `**Resolved At**: ${provenance.resolvedAt.toISOString()}\n\n`;
|
|
182
|
-
// Summary
|
|
183
|
-
md += `## Summary\n\n`;
|
|
184
|
-
md += `- Total Features: ${provenance.summary.totalFeatures}\n`;
|
|
185
|
-
md += `- Enabled: ${provenance.summary.enabled}\n`;
|
|
186
|
-
md += `- Blocked: ${provenance.summary.blocked}\n`;
|
|
187
|
-
md += `- Not Configured: ${provenance.summary.notConfigured}\n\n`;
|
|
188
|
-
// Sources
|
|
189
|
-
md += `## Configuration Sources\n\n`;
|
|
190
|
-
Object.entries(provenance.sources).forEach(([type, source]) => {
|
|
191
|
-
const sourceName = source.name || source.title || source.id;
|
|
192
|
-
md += `- **${type}**: ${sourceName} (${source.id})\n`;
|
|
193
|
-
});
|
|
194
|
-
md += `\n`;
|
|
195
|
-
// Features
|
|
196
|
-
md += `## Feature Resolution\n\n`;
|
|
197
|
-
for (const trail of provenance.features.values()) {
|
|
198
|
-
md += `### ${trail.featureId}\n\n`;
|
|
199
|
-
md += `**Status**: ${trail.finalState}\n\n`;
|
|
200
|
-
md += trail.explanation + `\n\n`;
|
|
201
|
-
}
|
|
202
|
-
// Decision log
|
|
203
|
-
md += `## Decision Log\n\n`;
|
|
204
|
-
provenance.decisionLog.forEach((decision) => {
|
|
205
|
-
md += `${decision.step}. **${decision.rule}** (precedence ${decision.precedence})\n`;
|
|
206
|
-
md += ` - Feature: ${decision.featureId}\n`;
|
|
207
|
-
md += ` - Action: ${decision.action}\n`;
|
|
208
|
-
md += ` - Source: ${decision.source.name || decision.source.type}\n`;
|
|
209
|
-
md += ` - Reason: ${decision.reason}\n\n`;
|
|
210
|
-
});
|
|
211
|
-
return md;
|
|
212
|
-
}
|
|
213
|
-
/**
|
|
214
|
-
* Format provenance as JSON (for APIs)
|
|
215
|
-
*/
|
|
216
|
-
export function formatProvenanceAsJSON(provenance) {
|
|
217
|
-
// Convert Map to object for JSON serialization
|
|
218
|
-
const serializable = {
|
|
219
|
-
...provenance,
|
|
220
|
-
features: Array.from(provenance.features.entries()).map(([_featureId, trail]) => trail),
|
|
221
|
-
};
|
|
222
|
-
return JSON.stringify(serializable, null, 2);
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Get simple explanation for a specific feature
|
|
226
|
-
*/
|
|
227
|
-
export function getFeatureExplanation(provenance, featureId) {
|
|
228
|
-
const trail = provenance.features.get(featureId);
|
|
229
|
-
return trail?.explanation || null;
|
|
230
|
-
}
|
|
231
|
-
//# sourceMappingURL=pnp-provenance.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"pnp-provenance.js","sourceRoot":"","sources":["../../src/services/pnp-provenance.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * PNP Resolution Provenance Tracking\n *\n * Provides detailed tracking of how PNP profiles are resolved, including:\n * - Decision steps at each precedence level\n * - Sources of configuration (district, assessment, item, student)\n * - Reasons for tool enablement/blocking\n * - Merge operations and overrides\n *\n * This enables:\n * - Visual explanation components for users\n * - Debugging tool resolution issues\n * - Compliance auditing and reporting\n * - Developer troubleshooting\n *\n * Part of PIE Assessment Toolkit.\n */\n\n/**\n * Decision made during PNP resolution\n */\nexport interface ResolutionDecision {\n\t/** Unique step identifier */\n\tstep: number;\n\n\t/** Precedence level (1-6, lower = higher priority) */\n\tprecedence: 1 | 2 | 3 | 4 | 5 | 6;\n\n\t/** Human-readable rule name */\n\trule:\n\t\t| \"district-block\"\n\t\t| \"test-admin-override\"\n\t\t| \"item-restriction\"\n\t\t| \"item-requirement\"\n\t\t| \"district-requirement\"\n\t\t| \"pnp-support\"\n\t\t| \"pnp-prohibited\"\n\t\t| \"system-default\";\n\n\t/** Tool or feature ID being resolved */\n\tfeatureId: string;\n\n\t/** Decision outcome */\n\taction: \"block\" | \"enable\" | \"skip\";\n\n\t/** Where the decision came from */\n\tsource: {\n\t\ttype:\n\t\t\t| \"organization\"\n\t\t\t| \"assessment\"\n\t\t\t| \"section\"\n\t\t\t| \"item\"\n\t\t\t| \"student\"\n\t\t\t| \"system\";\n\t\tid?: string; // Entity ID (e.g., org ID, assessment ID)\n\t\tname?: string; // Human-readable name\n\t};\n\n\t/** Why this decision was made */\n\treason: string;\n\n\t/** Configuration value that triggered this decision */\n\tvalue?: any;\n\n\t/** Timestamp of decision */\n\ttimestamp: Date;\n}\n\n/**\n * Complete resolution trail for a single feature\n */\nexport interface FeatureResolutionTrail {\n\t/** Feature ID (QTI 3.0 or tool ID) */\n\tfeatureId: string;\n\n\t/** Final decision */\n\tfinalState: \"enabled\" | \"blocked\" | \"not-configured\";\n\n\t/** Winning decision (highest precedence) */\n\twinningDecision?: ResolutionDecision;\n\n\t/** All decisions considered (in precedence order) */\n\tallDecisions: ResolutionDecision[];\n\n\t/** Human-readable explanation */\n\texplanation: string;\n}\n\n/**\n * Complete provenance for an entire PNP resolution\n */\nexport interface PnpResolutionProvenance {\n\t/** Session or resolution context ID */\n\tcontextId: string;\n\n\t/** When resolution occurred */\n\tresolvedAt: Date;\n\n\t/** All configuration sources consulted */\n\tsources: {\n\t\torganization?: {\n\t\t\tid: string;\n\t\t\tname: string;\n\t\t\tpnpDefaults?: any;\n\t\t};\n\t\tassessment?: {\n\t\t\tid: string;\n\t\t\tname: string;\n\t\t\tsettings?: any;\n\t\t};\n\t\tsection?: {\n\t\t\tid: string;\n\t\t\ttitle?: string;\n\t\t\tsettings?: any;\n\t\t};\n\t\titem?: {\n\t\t\tid: string;\n\t\t\tname?: string;\n\t\t\tsettings?: any;\n\t\t};\n\t\tstudent?: {\n\t\t\tid: string;\n\t\t\tpnpProfile?: any;\n\t\t};\n\t};\n\n\t/** Resolution trail for each feature */\n\tfeatures: Map<string, FeatureResolutionTrail>;\n\n\t/** All decisions in chronological order */\n\tdecisionLog: ResolutionDecision[];\n\n\t/** Summary statistics */\n\tsummary: {\n\t\ttotalFeatures: number;\n\t\tenabled: number;\n\t\tblocked: number;\n\t\tnotConfigured: number;\n\t\tbySource: Record<string, number>;\n\t\tbyRule: Record<string, number>;\n\t};\n}\n\n/**\n * Provenance builder for tracking PNP resolution\n */\nexport class PnpProvenanceBuilder {\n\tprivate provenance: PnpResolutionProvenance;\n\tprivate stepCounter = 0;\n\n\tconstructor(contextId: string) {\n\t\tthis.provenance = {\n\t\t\tcontextId,\n\t\t\tresolvedAt: new Date(),\n\t\t\tsources: {},\n\t\t\tfeatures: new Map(),\n\t\t\tdecisionLog: [],\n\t\t\tsummary: {\n\t\t\t\ttotalFeatures: 0,\n\t\t\t\tenabled: 0,\n\t\t\t\tblocked: 0,\n\t\t\t\tnotConfigured: 0,\n\t\t\t\tbySource: {},\n\t\t\t\tbyRule: {},\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Add a configuration source\n\t */\n\taddSource(\n\t\ttype: \"organization\" | \"assessment\" | \"section\" | \"item\" | \"student\",\n\t\tdata: { id: string; name?: string; config?: any },\n\t): this {\n\t\tthis.provenance.sources[type] = {\n\t\t\tid: data.id,\n\t\t\tname: data.name || data.id,\n\t\t\t...(type === \"organization\" && { pnpDefaults: data.config }),\n\t\t\t...(type === \"assessment\" && { settings: data.config }),\n\t\t\t...(type === \"section\" && { settings: data.config }),\n\t\t\t...(type === \"item\" && { settings: data.config }),\n\t\t\t...(type === \"student\" && { pnpProfile: data.config }),\n\t\t} as any;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Record a decision during resolution\n\t */\n\taddDecision(params: {\n\t\tprecedence: 1 | 2 | 3 | 4 | 5 | 6;\n\t\trule: ResolutionDecision[\"rule\"];\n\t\tfeatureId: string;\n\t\taction: \"block\" | \"enable\" | \"skip\";\n\t\tsourceType: ResolutionDecision[\"source\"][\"type\"];\n\t\tsourceId?: string;\n\t\tsourceName?: string;\n\t\treason: string;\n\t\tvalue?: any;\n\t}): this {\n\t\tconst decision: ResolutionDecision = {\n\t\t\tstep: ++this.stepCounter,\n\t\t\tprecedence: params.precedence,\n\t\t\trule: params.rule,\n\t\t\tfeatureId: params.featureId,\n\t\t\taction: params.action,\n\t\t\tsource: {\n\t\t\t\ttype: params.sourceType,\n\t\t\t\tid: params.sourceId,\n\t\t\t\tname: params.sourceName,\n\t\t\t},\n\t\t\treason: params.reason,\n\t\t\tvalue: params.value,\n\t\t\ttimestamp: new Date(),\n\t\t};\n\n\t\t// Add to decision log\n\t\tthis.provenance.decisionLog.push(decision);\n\n\t\t// Update feature trail\n\t\tif (!this.provenance.features.has(params.featureId)) {\n\t\t\tthis.provenance.features.set(params.featureId, {\n\t\t\t\tfeatureId: params.featureId,\n\t\t\t\tfinalState: \"not-configured\",\n\t\t\t\tallDecisions: [],\n\t\t\t\texplanation: \"\",\n\t\t\t});\n\t\t}\n\n\t\tconst trail = this.provenance.features.get(params.featureId)!;\n\t\ttrail.allDecisions.push(decision);\n\n\t\t// Update winning decision if this has higher precedence\n\t\tif (\n\t\t\tparams.action !== \"skip\" &&\n\t\t\t(!trail.winningDecision ||\n\t\t\t\tdecision.precedence < trail.winningDecision.precedence)\n\t\t) {\n\t\t\ttrail.winningDecision = decision;\n\t\t\ttrail.finalState = decision.action === \"enable\" ? \"enabled\" : \"blocked\";\n\t\t}\n\n\t\t// Update summary\n\t\tthis.provenance.summary.bySource[params.sourceType] =\n\t\t\t(this.provenance.summary.bySource[params.sourceType] || 0) + 1;\n\t\tthis.provenance.summary.byRule[params.rule] =\n\t\t\t(this.provenance.summary.byRule[params.rule] || 0) + 1;\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Build final provenance with explanations\n\t */\n\tbuild(): PnpResolutionProvenance {\n\t\t// Generate explanations for each feature\n\t\tfor (const [featureId, trail] of this.provenance.features.entries()) {\n\t\t\ttrail.explanation = this.generateExplanation(trail);\n\t\t}\n\n\t\t// Update summary counts\n\t\tfor (const trail of this.provenance.features.values()) {\n\t\t\tthis.provenance.summary.totalFeatures++;\n\t\t\tif (trail.finalState === \"enabled\") {\n\t\t\t\tthis.provenance.summary.enabled++;\n\t\t\t} else if (trail.finalState === \"blocked\") {\n\t\t\t\tthis.provenance.summary.blocked++;\n\t\t\t} else {\n\t\t\t\tthis.provenance.summary.notConfigured++;\n\t\t\t}\n\t\t}\n\n\t\treturn this.provenance;\n\t}\n\n\t/**\n\t * Generate human-readable explanation for a feature resolution\n\t */\n\tprivate generateExplanation(trail: FeatureResolutionTrail): string {\n\t\tif (!trail.winningDecision) {\n\t\t\treturn `Feature \"${trail.featureId}\" was not configured at any level.`;\n\t\t}\n\n\t\tconst decision = trail.winningDecision;\n\t\tconst source = decision.source;\n\t\tconst sourceName = source.name || source.type;\n\n\t\tlet explanation = `Feature \"${trail.featureId}\" is ${trail.finalState}.\\n\\n`;\n\n\t\t// Explain winning decision\n\t\texplanation += `**Primary Reason** (${this.getRuleName(decision.rule)}):\\n`;\n\t\texplanation += `${decision.reason}\\n`;\n\t\texplanation += `Source: ${sourceName} (${source.type})\\n\\n`;\n\n\t\t// Show overridden decisions if any\n\t\tconst overridden = trail.allDecisions.filter(\n\t\t\t(d) =>\n\t\t\t\td.step !== decision.step &&\n\t\t\t\td.action !== \"skip\" &&\n\t\t\t\td.precedence > decision.precedence,\n\t\t);\n\n\t\tif (overridden.length > 0) {\n\t\t\texplanation += `**Overridden Rules**:\\n`;\n\t\t\toverridden.forEach((d) => {\n\t\t\t\texplanation += `- ${this.getRuleName(d.rule)}: ${d.reason} (overridden by higher precedence)\\n`;\n\t\t\t});\n\t\t}\n\n\t\treturn explanation;\n\t}\n\n\t/**\n\t * Get human-readable rule name\n\t */\n\tprivate getRuleName(rule: ResolutionDecision[\"rule\"]): string {\n\t\tconst names: Record<ResolutionDecision[\"rule\"], string> = {\n\t\t\t\"district-block\": \"District Block\",\n\t\t\t\"test-admin-override\": \"Test Administrator Override\",\n\t\t\t\"item-restriction\": \"Item Restriction\",\n\t\t\t\"item-requirement\": \"Item Requirement\",\n\t\t\t\"district-requirement\": \"District Requirement\",\n\t\t\t\"pnp-support\": \"Student PNP Support\",\n\t\t\t\"pnp-prohibited\": \"Student PNP Prohibition\",\n\t\t\t\"system-default\": \"System Default\",\n\t\t};\n\t\treturn names[rule];\n\t}\n\n\t/**\n\t * Get current provenance (without explanations)\n\t */\n\tgetCurrent(): PnpResolutionProvenance {\n\t\treturn this.provenance;\n\t}\n}\n\n/**\n * Format provenance as markdown for display\n */\nexport function formatProvenanceAsMarkdown(\n\tprovenance: PnpResolutionProvenance,\n): string {\n\tlet md = `# PNP Resolution Report\\n\\n`;\n\tmd += `**Context**: ${provenance.contextId}\\n`;\n\tmd += `**Resolved At**: ${provenance.resolvedAt.toISOString()}\\n\\n`;\n\n\t// Summary\n\tmd += `## Summary\\n\\n`;\n\tmd += `- Total Features: ${provenance.summary.totalFeatures}\\n`;\n\tmd += `- Enabled: ${provenance.summary.enabled}\\n`;\n\tmd += `- Blocked: ${provenance.summary.blocked}\\n`;\n\tmd += `- Not Configured: ${provenance.summary.notConfigured}\\n\\n`;\n\n\t// Sources\n\tmd += `## Configuration Sources\\n\\n`;\n\tObject.entries(provenance.sources).forEach(([type, source]) => {\n\t\tconst sourceName =\n\t\t\t(source as any).name || (source as any).title || source.id;\n\t\tmd += `- **${type}**: ${sourceName} (${source.id})\\n`;\n\t});\n\tmd += `\\n`;\n\n\t// Features\n\tmd += `## Feature Resolution\\n\\n`;\n\tfor (const trail of provenance.features.values()) {\n\t\tmd += `### ${trail.featureId}\\n\\n`;\n\t\tmd += `**Status**: ${trail.finalState}\\n\\n`;\n\t\tmd += trail.explanation + `\\n\\n`;\n\t}\n\n\t// Decision log\n\tmd += `## Decision Log\\n\\n`;\n\tprovenance.decisionLog.forEach((decision) => {\n\t\tmd += `${decision.step}. **${decision.rule}** (precedence ${decision.precedence})\\n`;\n\t\tmd += ` - Feature: ${decision.featureId}\\n`;\n\t\tmd += ` - Action: ${decision.action}\\n`;\n\t\tmd += ` - Source: ${decision.source.name || decision.source.type}\\n`;\n\t\tmd += ` - Reason: ${decision.reason}\\n\\n`;\n\t});\n\n\treturn md;\n}\n\n/**\n * Format provenance as JSON (for APIs)\n */\nexport function formatProvenanceAsJSON(\n\tprovenance: PnpResolutionProvenance,\n): string {\n\t// Convert Map to object for JSON serialization\n\tconst serializable = {\n\t\t...provenance,\n\t\tfeatures: Array.from(provenance.features.entries()).map(\n\t\t\t([_featureId, trail]) => trail,\n\t\t),\n\t};\n\treturn JSON.stringify(serializable, null, 2);\n}\n\n/**\n * Get simple explanation for a specific feature\n */\nexport function getFeatureExplanation(\n\tprovenance: PnpResolutionProvenance,\n\tfeatureId: string,\n): string | null {\n\tconst trail = provenance.features.get(featureId);\n\treturn trail?.explanation || null;\n}\n"]}
|