@pie-players/pie-assessment-toolkit 0.3.73 → 0.3.75
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 +469 -318
- package/dist/attempt/AssessmentSession.d.ts +1 -5
- package/dist/attempt/AssessmentSession.js +0 -29
- package/dist/attempt/TestSession.d.ts +1 -20
- package/dist/attempt/TestSession.js +8 -99
- package/dist/components/ItemScope.custom-element.d.ts +1 -0
- package/dist/components/ItemScope.custom-element.js +1 -0
- 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 +13 -13
- package/dist/components/SectionToolBar.custom-element.d.ts +1 -0
- package/dist/components/SectionToolBar.custom-element.js +1 -1
- package/dist/components/chunks/ItemToolBar-7tt7v2xp.js +4 -0
- package/dist/components/chunks/ItemToolBar-97hx60z4.js +2 -0
- package/dist/components/chunks/ItemToolBar-bxwyhr5p.js +2 -0
- package/dist/components/chunks/ItemToolBar-jx38w99s.js +2 -0
- package/dist/components/chunks/ItemToolBar-rd2bb4p6.js +40 -0
- package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
- package/dist/components/chunks/sre-engine-jd8cc08x.js +1 -0
- package/dist/components/item-scope-element.d.ts +2 -0
- package/dist/components/item-scope-element.js +2 -0
- package/dist/context/assessment-toolkit-context.d.ts +32 -6
- package/dist/index.d.ts +23 -48
- package/dist/index.js +12 -37
- package/dist/policy/core/PolicySource.d.ts +1 -1
- package/dist/policy/core/ToolPolicyEngine.d.ts +85 -34
- package/dist/policy/core/ToolPolicyEngine.js +148 -46
- package/dist/policy/core/compose-decision.d.ts +28 -7
- package/dist/policy/core/compose-decision.js +75 -54
- package/dist/policy/core/decision-types.d.ts +65 -34
- package/dist/policy/core/feature-decision.d.ts +16 -9
- package/dist/policy/core/feature-decision.js +14 -8
- package/dist/policy/core/pnp-policy-inputs.d.ts +24 -33
- package/dist/policy/core/pnp-policy-inputs.js +28 -56
- package/dist/policy/core/provenance.d.ts +9 -4
- package/dist/policy/core/provenance.js +1 -1
- package/dist/policy/engine.d.ts +7 -23
- package/dist/policy/engine.js +5 -20
- package/dist/policy/sources/PnpPolicySource.d.ts +44 -61
- package/dist/policy/sources/PnpPolicySource.js +117 -118
- package/dist/runtime/SectionControllerBinding.d.ts +279 -0
- package/dist/runtime/SectionControllerBinding.js +450 -0
- package/dist/runtime/SectionRuntimeEngine.d.ts +26 -289
- package/dist/runtime/SectionRuntimeEngine.js +30 -372
- package/dist/runtime/adapter/SectionEngineAdapter.d.ts +19 -80
- package/dist/runtime/adapter/SectionEngineAdapter.js +19 -92
- package/dist/runtime/adapter/dom-event-bridge.d.ts +15 -30
- package/dist/runtime/adapter/dom-event-bridge.js +17 -44
- package/dist/runtime/adapter/subscriber-fanout.d.ts +1 -2
- package/dist/runtime/adapter/subscriber-fanout.js +1 -2
- package/dist/runtime/catalog-context.d.ts +19 -0
- package/dist/runtime/catalog-context.js +28 -0
- package/dist/runtime/composition-emit-scheduler.d.ts +9 -15
- package/dist/runtime/composition-emit-scheduler.js +9 -15
- package/dist/runtime/content-language.d.ts +32 -0
- package/dist/runtime/content-language.js +46 -0
- package/dist/runtime/content-region.d.ts +17 -0
- package/dist/runtime/content-region.js +29 -0
- package/dist/runtime/content-registration.d.ts +13 -0
- package/dist/runtime/content-registration.js +25 -0
- package/dist/runtime/core/SectionEngineCore.d.ts +2 -2
- package/dist/runtime/core/SectionEngineCore.js +2 -2
- package/dist/runtime/core/cohort.d.ts +1 -1
- package/dist/runtime/core/cohort.js +1 -1
- package/dist/runtime/core/engine-input.d.ts +18 -30
- package/dist/runtime/core/engine-input.js +11 -12
- package/dist/runtime/core/engine-output.d.ts +16 -40
- package/dist/runtime/core/engine-output.js +9 -21
- package/dist/runtime/core/engine-readiness.d.ts +5 -37
- package/dist/runtime/core/engine-readiness.js +8 -41
- package/dist/runtime/core/engine-resolver.d.ts +46 -74
- package/dist/runtime/core/engine-resolver.js +18 -43
- package/dist/runtime/core/engine-state.d.ts +28 -36
- package/dist/runtime/core/engine-state.js +18 -21
- package/dist/runtime/core/engine-transition.d.ts +14 -19
- package/dist/runtime/core/engine-transition.js +71 -51
- package/dist/runtime/engine.d.ts +16 -19
- package/dist/runtime/engine.js +12 -19
- package/dist/runtime/page-warnings.d.ts +13 -0
- package/dist/runtime/page-warnings.js +22 -0
- package/dist/runtime/policy-input-forwarding.d.ts +25 -0
- package/dist/runtime/policy-input-forwarding.js +25 -0
- package/dist/runtime/registration-events.d.ts +14 -0
- package/dist/runtime/runtime-event-claim.d.ts +8 -0
- package/dist/runtime/runtime-event-claim.js +15 -0
- package/dist/runtime/shell-event-bridge.d.ts +45 -0
- package/dist/runtime/shell-event-bridge.js +127 -0
- package/dist/runtime/shell-registration.d.ts +73 -0
- package/dist/runtime/shell-registration.js +95 -0
- package/dist/runtime/shell-scope.d.ts +68 -0
- package/dist/runtime/shell-scope.js +188 -0
- package/dist/runtime/tool-host-contract.d.ts +28 -10
- package/dist/runtime/tool-host-contract.js +31 -10
- package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
- package/dist/runtime/unclaimed-registration-watch.js +23 -0
- package/dist/services/ElementToolStateStore.d.ts +14 -10
- package/dist/services/ElementToolStateStore.js +37 -13
- package/dist/services/HighlightCoordinator.d.ts +22 -28
- package/dist/services/HighlightCoordinator.js +431 -327
- package/dist/services/RangeSerializer.d.ts +10 -1
- package/dist/services/RangeSerializer.js +87 -14
- package/dist/services/TTSService.d.ts +130 -69
- package/dist/services/TTSService.js +713 -570
- package/dist/services/ToolCoordinator.d.ts +43 -66
- package/dist/services/ToolCoordinator.js +164 -208
- package/dist/services/ToolRegistry.d.ts +78 -54
- package/dist/services/ToolRegistry.js +107 -105
- package/dist/services/ToolkitCoordinator.d.ts +261 -175
- package/dist/services/ToolkitCoordinator.js +667 -433
- package/dist/services/audio-handoff.d.ts +6 -7
- package/dist/services/audio-handoff.js +20 -14
- package/dist/services/catalog-owner.d.ts +11 -1
- package/dist/services/catalog-owner.js +25 -0
- package/dist/services/create-tools-config.d.ts +2 -2
- package/dist/services/framework-error-bus.d.ts +3 -6
- package/dist/services/framework-error-bus.js +3 -6
- package/dist/services/framework-error.d.ts +30 -8
- package/dist/services/framework-error.js +9 -7
- package/dist/services/interfaces.d.ts +158 -129
- package/dist/services/section-controller-types.d.ts +15 -0
- package/dist/services/section-session-assignment.d.ts +21 -0
- package/dist/services/section-session-assignment.js +82 -0
- package/dist/services/spoken-audio-cards.d.ts +0 -2
- package/dist/services/spoken-audio-cards.js +1 -3
- package/dist/services/tool-config-defaults.d.ts +3 -3
- package/dist/services/tool-config-defaults.js +3 -3
- package/dist/services/tool-config-validation.d.ts +14 -4
- package/dist/services/tool-config-validation.js +63 -71
- package/dist/services/tool-context.d.ts +0 -8
- package/dist/services/tool-context.js +29 -25
- package/dist/services/tool-icons.d.ts +4 -3
- package/dist/services/tool-icons.js +4 -3
- package/dist/services/tool-instance-id.d.ts +0 -1
- package/dist/services/tool-instance-id.js +0 -6
- package/dist/services/tool-providers/TTSToolProvider.d.ts +41 -80
- package/dist/services/tool-providers/TTSToolProvider.js +81 -40
- package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -4
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts +32 -28
- package/dist/services/tool-providers/ToolProviderRegistry.js +121 -107
- package/dist/services/tool-providers/index.d.ts +0 -9
- package/dist/services/tool-providers/index.js +2 -4
- package/dist/services/tool-request.d.ts +27 -12
- package/dist/services/tool-request.js +24 -14
- package/dist/services/toolbar-decided-tools.d.ts +15 -0
- package/dist/services/toolbar-decided-tools.js +28 -0
- package/dist/services/tools-config-normalizer.d.ts +37 -23
- package/dist/services/tools-config-normalizer.js +2 -10
- package/dist/services/tts/browser-provider.d.ts +38 -2
- package/dist/services/tts/browser-provider.js +276 -140
- 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/flat-tree.d.ts +71 -0
- package/dist/services/tts/flat-tree.js +269 -0
- package/dist/services/tts/generated-speech/assemble-plan.d.ts +3 -1
- package/dist/services/tts/generated-speech/assemble-plan.js +3 -1
- package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +2 -0
- package/dist/services/tts/generated-speech/dom/build-dom-plan.js +1 -1
- package/dist/services/tts/generated-speech/math-speech-cache.d.ts +3 -1
- package/dist/services/tts/generated-speech/math-speech-cache.js +5 -1
- 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 +38 -5
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +3 -6
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +6 -4
- package/dist/services/tts/highlight-pipeline/readable-region.js +3 -2
- package/dist/services/tts/highlight-pipeline/types.d.ts +5 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +12 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.js +73 -7
- 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.d.ts +5 -1
- package/dist/services/tts/math-aware-text-processing.js +74 -18
- package/dist/services/tts/math-control-names.d.ts +21 -0
- package/dist/services/tts/math-control-names.js +142 -0
- package/dist/services/tts/math-speech.d.ts +16 -1
- package/dist/services/tts/math-speech.js +55 -19
- package/dist/services/tts/provider-options.d.ts +41 -0
- package/dist/services/tts/provider-options.js +1 -0
- 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/ssml/spoken-text.d.ts +2 -26
- package/dist/services/tts/ssml/spoken-text.js +29 -0
- package/dist/services/tts/start-failure.d.ts +11 -0
- package/dist/services/tts/start-failure.js +19 -0
- package/dist/services/tts/text-processing.d.ts +9 -1
- package/dist/services/tts/text-processing.js +46 -34
- package/dist/services/tts/text-segmentation.d.ts +7 -0
- package/dist/services/tts/text-segmentation.js +47 -0
- package/dist/services/tts-runtime-config.d.ts +44 -18
- package/dist/services/tts-runtime-config.js +22 -93
- package/dist/tools/content-capability-resolution.d.ts +7 -9
- package/dist/tools/content-capability-resolution.js +8 -45
- package/dist/tools/registration.d.ts +58 -0
- package/dist/tools/registration.js +54 -0
- package/dist/tools/registrations/toolbar-registration-helpers.js +2 -2
- package/dist/tools/tool-surface-host.d.ts +6 -10
- package/dist/tools/tool-surface-host.js +44 -42
- package/dist/tools/tool-tag-map.d.ts +5 -15
- package/dist/tools/tool-tag-map.js +6 -20
- package/dist/utils/structural-equality.d.ts +11 -0
- package/dist/utils/structural-equality.js +47 -0
- package/package.json +24 -59
- package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -49
- package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -138
- package/dist/components/chunks/ItemToolBar-byq80z42.js +0 -40
- package/dist/components/chunks/ItemToolBar-d36g3aw4.js +0 -49
- package/dist/context/runtime-context-consumer.d.ts +0 -13
- package/dist/context/runtime-context-consumer.js +0 -51
- package/dist/item-loader.d.ts +0 -44
- package/dist/item-loader.js +0 -61
- package/dist/policy/core/engine-context.d.ts +0 -29
- package/dist/policy/core/engine-context.js +0 -27
- package/dist/policy/core/policy-source-tag.d.ts +0 -31
- package/dist/policy/core/policy-source-tag.js +0 -28
- package/dist/policy/internal.d.ts +0 -25
- package/dist/policy/internal.js +0 -25
- package/dist/runtime/adapter/coordinator-bridge.d.ts +0 -92
- package/dist/runtime/adapter/coordinator-bridge.js +0 -126
- package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -36
- package/dist/runtime/adapter/framework-error-bridge.js +0 -46
- package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -31
- package/dist/runtime/adapter/instrumentation-bridge.js +0 -38
- package/dist/runtime/core/engine-stage-derivation.d.ts +0 -18
- package/dist/runtime/core/engine-stage-derivation.js +0 -29
- package/dist/runtime/engine-context.d.ts +0 -39
- package/dist/runtime/engine-context.js +0 -31
- package/dist/runtime/internal.d.ts +0 -43
- package/dist/runtime/internal.js +0 -45
- package/dist/runtime/section-runtime-engine-host-context.d.ts +0 -80
- package/dist/runtime/section-runtime-engine-host-context.js +0 -95
- package/dist/runtime/stage-emit-gate.d.ts +0 -92
- package/dist/runtime/stage-emit-gate.js +0 -82
- package/dist/services/I18nService.d.ts +0 -47
- package/dist/services/I18nService.js +0 -84
- package/dist/services/ThemeProvider.d.ts +0 -95
- package/dist/services/ThemeProvider.js +0 -334
- 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/defaultPersonalNeedsProfile.d.ts +0 -21
- package/dist/services/defaultPersonalNeedsProfile.js +0 -26
- package/dist/services/pnp-standard-features.d.ts +0 -238
- package/dist/services/pnp-standard-features.js +0 -338
- 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/client.d.ts +0 -12
- package/dist/tools/client.js +0 -14
- package/dist/tools/index.d.ts +0 -8
- package/dist/tools/index.js +0 -10
- package/dist/tools/internal.d.ts +0 -43
- package/dist/tools/internal.js +0 -41
- 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
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* and button/instance creation. Supports dynamic registration and override by integrators.
|
|
6
6
|
*/
|
|
7
7
|
import { dynamicMessageKey } from "@pie-players/pie-players-shared/i18n/provider";
|
|
8
|
-
import {
|
|
8
|
+
import { resolveToolTag, } from "../tools/tool-tag-map.js";
|
|
9
|
+
import { PENDING_INPUT_WARNING_DELAY_MS, warnOncePerDocument, } from "../runtime/page-warnings.js";
|
|
9
10
|
/**
|
|
10
11
|
* A registration's display name in the interface locale.
|
|
11
12
|
*
|
|
@@ -13,16 +14,12 @@ import { normalizeToolAlias } from "./tools-config-normalizer.js";
|
|
|
13
14
|
* falls back to `name` rather than rendering the key, so a catalog gap degrades
|
|
14
15
|
* to English instead of to `tools.something.name` on a toolbar button.
|
|
15
16
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
17
|
+
* It lives here so the toolbars, the settings panels and the PNP debugger
|
|
18
|
+
* cannot each invent their own precedence.
|
|
18
19
|
*/
|
|
19
20
|
export function resolveToolRegistrationName(registration, i18n) {
|
|
20
21
|
return resolveKeyedString(registration.name, registration.nameKey, i18n);
|
|
21
22
|
}
|
|
22
|
-
/** A registration's description in the interface locale. See the name resolver. */
|
|
23
|
-
export function resolveToolRegistrationDescription(registration, i18n) {
|
|
24
|
-
return resolveKeyedString(registration.description, registration.descriptionKey, i18n);
|
|
25
|
-
}
|
|
26
23
|
function resolveKeyedString(source, key, i18n) {
|
|
27
24
|
if (!key || !i18n)
|
|
28
25
|
return source;
|
|
@@ -50,7 +47,7 @@ function assertNonEmptyString(value, fieldName) {
|
|
|
50
47
|
}
|
|
51
48
|
// Defence-in-depth: reject obvious XSS payloads in tool-registered icon
|
|
52
49
|
// markup at registration time. Runtime rendering still runs each icon
|
|
53
|
-
// through DOMPurify (
|
|
50
|
+
// through DOMPurify (`sanitizeSvgIcon` in `ItemToolBar.svelte`), but surfacing the problem
|
|
54
51
|
// early produces a clearer error for tool authors than "the icon silently
|
|
55
52
|
// disappeared after sanitization".
|
|
56
53
|
const SCRIPTABLE_ICON_PATTERNS = [
|
|
@@ -151,11 +148,6 @@ function assertToolRegistrationShape(registration) {
|
|
|
151
148
|
registration.singletonScope !== "section") {
|
|
152
149
|
throw new Error(`Invalid tool registration "${registration.toolId}": selection-gateway tools must declare singletonScope "section".`);
|
|
153
150
|
}
|
|
154
|
-
if (registration.pnpSupportIds !== undefined &&
|
|
155
|
-
(!Array.isArray(registration.pnpSupportIds) ||
|
|
156
|
-
registration.pnpSupportIds.some((pnpId) => typeof pnpId !== "string" || pnpId.trim().length === 0))) {
|
|
157
|
-
throw new Error(`Invalid tool registration "${registration.toolId}": "pnpSupportIds" must be an array of non-empty strings.`);
|
|
158
|
-
}
|
|
159
151
|
if (registration.activation !== "region" &&
|
|
160
152
|
typeof registration.isVisibleInContext !== "function") {
|
|
161
153
|
throw new Error(`Invalid tool registration "${registration.toolId}": "isVisibleInContext" must be a function.`);
|
|
@@ -174,12 +166,6 @@ function assertToolRegistrationShape(registration) {
|
|
|
174
166
|
typeof registration.requiresAuthoredContent.resolve !== "function") {
|
|
175
167
|
throw new Error(`Invalid tool registration "${registration.toolId}": "requiresAuthoredContent" must be an object with a "resolve" function.`);
|
|
176
168
|
}
|
|
177
|
-
if (!registration.pnpSupportIds?.length) {
|
|
178
|
-
// A content dependency's second job is keeping the capability out of a
|
|
179
|
-
// wholesale grant, and a host filters that by support id. Declaring one
|
|
180
|
-
// with no id to filter on would silently drop that guarantee.
|
|
181
|
-
throw new Error(`Invalid tool registration "${registration.toolId}": "requiresAuthoredContent" requires at least one entry in "pnpSupportIds", which is what a host filters a default grant list on.`);
|
|
182
|
-
}
|
|
183
169
|
}
|
|
184
170
|
if (registration.renderToolbar !== undefined) {
|
|
185
171
|
if (typeof registration.renderToolbar !== "function") {
|
|
@@ -197,10 +183,12 @@ function assertToolRegistrationShape(registration) {
|
|
|
197
183
|
*/
|
|
198
184
|
export class ToolRegistry {
|
|
199
185
|
tools = new Map();
|
|
200
|
-
pnpIndex = new Map(); // pnpSupportId → Set<toolId>
|
|
201
186
|
componentOverrides = {};
|
|
187
|
+
watchedUndefinedToolElements = new Set();
|
|
202
188
|
moduleLoaders = new Map();
|
|
203
189
|
loadedToolModules = new Set();
|
|
190
|
+
warnedLoaderReplacements = new Set();
|
|
191
|
+
warnedUnregisteredAllowedTools = new Set();
|
|
204
192
|
moduleLoadPromises = new Map();
|
|
205
193
|
changeListeners = new Set();
|
|
206
194
|
emitChange(event) {
|
|
@@ -228,13 +216,13 @@ export class ToolRegistry {
|
|
|
228
216
|
};
|
|
229
217
|
}
|
|
230
218
|
/**
|
|
231
|
-
* Normalize a single tool
|
|
219
|
+
* Normalize a single tool id (trims surrounding whitespace).
|
|
232
220
|
*/
|
|
233
221
|
normalizeToolId(toolId) {
|
|
234
|
-
return
|
|
222
|
+
return toolId.trim();
|
|
235
223
|
}
|
|
236
224
|
/**
|
|
237
|
-
* Normalize a list of tool
|
|
225
|
+
* Normalize a list of tool ids (trims surrounding whitespace).
|
|
238
226
|
*/
|
|
239
227
|
normalizeToolIds(toolIds) {
|
|
240
228
|
return toolIds.map((toolId) => this.normalizeToolId(toolId));
|
|
@@ -251,15 +239,6 @@ export class ToolRegistry {
|
|
|
251
239
|
throw new Error(`Tool '${registration.toolId}' is already registered`);
|
|
252
240
|
}
|
|
253
241
|
this.tools.set(registration.toolId, registration);
|
|
254
|
-
// Index PNP support IDs
|
|
255
|
-
if (registration.pnpSupportIds) {
|
|
256
|
-
for (const pnpId of registration.pnpSupportIds) {
|
|
257
|
-
if (!this.pnpIndex.has(pnpId)) {
|
|
258
|
-
this.pnpIndex.set(pnpId, new Set());
|
|
259
|
-
}
|
|
260
|
-
this.pnpIndex.get(pnpId).add(registration.toolId);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
242
|
this.emitChange({ kind: "register", toolIds: [registration.toolId] });
|
|
264
243
|
}
|
|
265
244
|
/**
|
|
@@ -272,24 +251,7 @@ export class ToolRegistry {
|
|
|
272
251
|
if (!this.tools.has(registration.toolId)) {
|
|
273
252
|
throw new Error(`Cannot override non-existent tool '${registration.toolId}'`);
|
|
274
253
|
}
|
|
275
|
-
// Remove old PNP index entries
|
|
276
|
-
const oldReg = this.tools.get(registration.toolId);
|
|
277
|
-
if (oldReg.pnpSupportIds) {
|
|
278
|
-
for (const pnpId of oldReg.pnpSupportIds) {
|
|
279
|
-
this.pnpIndex.get(pnpId)?.delete(registration.toolId);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
// Add new registration
|
|
283
254
|
this.tools.set(registration.toolId, registration);
|
|
284
|
-
// Re-index PNP support IDs
|
|
285
|
-
if (registration.pnpSupportIds) {
|
|
286
|
-
for (const pnpId of registration.pnpSupportIds) {
|
|
287
|
-
if (!this.pnpIndex.has(pnpId)) {
|
|
288
|
-
this.pnpIndex.set(pnpId, new Set());
|
|
289
|
-
}
|
|
290
|
-
this.pnpIndex.get(pnpId).add(registration.toolId);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
255
|
this.emitChange({ kind: "override", toolIds: [registration.toolId] });
|
|
294
256
|
}
|
|
295
257
|
/**
|
|
@@ -298,15 +260,8 @@ export class ToolRegistry {
|
|
|
298
260
|
* @param toolId - Tool ID to remove
|
|
299
261
|
*/
|
|
300
262
|
unregister(toolId) {
|
|
301
|
-
|
|
302
|
-
if (!reg)
|
|
263
|
+
if (!this.tools.has(toolId))
|
|
303
264
|
return;
|
|
304
|
-
// Remove PNP index entries
|
|
305
|
-
if (reg.pnpSupportIds) {
|
|
306
|
-
for (const pnpId of reg.pnpSupportIds) {
|
|
307
|
-
this.pnpIndex.get(pnpId)?.delete(toolId);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
265
|
this.tools.delete(toolId);
|
|
311
266
|
this.emitChange({ kind: "unregister", toolIds: [toolId] });
|
|
312
267
|
}
|
|
@@ -344,15 +299,6 @@ export class ToolRegistry {
|
|
|
344
299
|
getAllTools() {
|
|
345
300
|
return Array.from(this.tools.values());
|
|
346
301
|
}
|
|
347
|
-
/**
|
|
348
|
-
* Find tool IDs that support a given PNP support ID
|
|
349
|
-
*
|
|
350
|
-
* @param pnpSupportId - PNP support ID (e.g., 'calculator')
|
|
351
|
-
* @returns Set of tool IDs that support this PNP ID
|
|
352
|
-
*/
|
|
353
|
-
getToolsByPNPSupport(pnpSupportId) {
|
|
354
|
-
return this.pnpIndex.get(pnpSupportId) || new Set();
|
|
355
|
-
}
|
|
356
302
|
/**
|
|
357
303
|
* Get tools that support a specific level
|
|
358
304
|
*
|
|
@@ -389,21 +335,18 @@ export class ToolRegistry {
|
|
|
389
335
|
tool.surfaces?.includes(surface));
|
|
390
336
|
}
|
|
391
337
|
/**
|
|
392
|
-
* Support ids
|
|
338
|
+
* Support ids, which are tool ids, of the capabilities that need authored
|
|
339
|
+
* content.
|
|
393
340
|
*
|
|
394
|
-
* What a host filters a default grant list on
|
|
395
|
-
*
|
|
396
|
-
*
|
|
341
|
+
* What a host filters a default grant list on: granting one of these
|
|
342
|
+
* wholesale grants an accommodation to learners with no documented need
|
|
343
|
+
* for it.
|
|
397
344
|
*/
|
|
398
345
|
getContentDependentSupportIds() {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
for (const supportId of tool.pnpSupportIds || [])
|
|
404
|
-
ids.add(supportId);
|
|
405
|
-
}
|
|
406
|
-
return [...ids].sort();
|
|
346
|
+
return this.getAllTools()
|
|
347
|
+
.filter((tool) => tool.requiresAuthoredContent)
|
|
348
|
+
.map((tool) => tool.toolId)
|
|
349
|
+
.sort();
|
|
407
350
|
}
|
|
408
351
|
/**
|
|
409
352
|
* Filter tool IDs by activation type.
|
|
@@ -414,7 +357,7 @@ export class ToolRegistry {
|
|
|
414
357
|
/**
|
|
415
358
|
* Filter tools by visibility in a given context
|
|
416
359
|
*
|
|
417
|
-
* Pass 2 of the
|
|
360
|
+
* Pass 2 of the three-pass model: Given a list of allowed tool IDs (from Pass 1),
|
|
418
361
|
* ask each tool if it's relevant in this context.
|
|
419
362
|
*
|
|
420
363
|
* @param allowedToolIds - Tool IDs that passed Pass 1 (orchestrator approval)
|
|
@@ -426,7 +369,11 @@ export class ToolRegistry {
|
|
|
426
369
|
for (const toolId of allowedToolIds) {
|
|
427
370
|
const tool = this.get(toolId);
|
|
428
371
|
if (!tool) {
|
|
429
|
-
|
|
372
|
+
// Toolbars filter on every render; one warning per id carries it.
|
|
373
|
+
if (!this.warnedUnregisteredAllowedTools.has(toolId)) {
|
|
374
|
+
this.warnedUnregisteredAllowedTools.add(toolId);
|
|
375
|
+
console.warn(`Tool '${toolId}' is allowed but not registered`);
|
|
376
|
+
}
|
|
430
377
|
continue;
|
|
431
378
|
}
|
|
432
379
|
// Check if tool supports this level
|
|
@@ -480,14 +427,13 @@ export class ToolRegistry {
|
|
|
480
427
|
* Get tool metadata for building UIs
|
|
481
428
|
* Useful for building PNP configuration interfaces
|
|
482
429
|
*
|
|
483
|
-
* @returns Array of tool metadata (id, name, description,
|
|
430
|
+
* @returns Array of tool metadata (id, name, description, levels, activation)
|
|
484
431
|
*/
|
|
485
432
|
getToolMetadata() {
|
|
486
433
|
return this.getAllTools().map((tool) => ({
|
|
487
434
|
toolId: tool.toolId,
|
|
488
435
|
name: tool.name,
|
|
489
436
|
description: tool.description,
|
|
490
|
-
pnpSupportIds: tool.pnpSupportIds || [],
|
|
491
437
|
supportedLevels: tool.supportedLevels,
|
|
492
438
|
activation: tool.activation || "toolbar-toggle",
|
|
493
439
|
singletonScope: tool.singletonScope || null,
|
|
@@ -496,25 +442,6 @@ export class ToolRegistry {
|
|
|
496
442
|
contentDependencyDescription: tool.requiresAuthoredContent?.description ?? null,
|
|
497
443
|
}));
|
|
498
444
|
}
|
|
499
|
-
/**
|
|
500
|
-
* Generate PNP support IDs from enabled tools
|
|
501
|
-
* Useful for creating PNP profiles
|
|
502
|
-
*
|
|
503
|
-
* @param enabledToolIds - Tool IDs to enable
|
|
504
|
-
* @returns Array of unique PNP support IDs
|
|
505
|
-
*/
|
|
506
|
-
generatePNPSupportsFromTools(enabledToolIds) {
|
|
507
|
-
const pnpSupports = new Set();
|
|
508
|
-
for (const toolId of enabledToolIds) {
|
|
509
|
-
const tool = this.get(toolId);
|
|
510
|
-
if (tool?.pnpSupportIds) {
|
|
511
|
-
for (const pnpId of tool.pnpSupportIds) {
|
|
512
|
-
pnpSupports.add(pnpId);
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
return Array.from(pnpSupports);
|
|
517
|
-
}
|
|
518
445
|
/**
|
|
519
446
|
* Clear all registrations (useful for testing)
|
|
520
447
|
*/
|
|
@@ -523,7 +450,6 @@ export class ToolRegistry {
|
|
|
523
450
|
if (toolIds.length === 0)
|
|
524
451
|
return;
|
|
525
452
|
this.tools.clear();
|
|
526
|
-
this.pnpIndex.clear();
|
|
527
453
|
this.emitChange({ kind: "clear", toolIds });
|
|
528
454
|
}
|
|
529
455
|
/**
|
|
@@ -538,6 +464,8 @@ export class ToolRegistry {
|
|
|
538
464
|
/**
|
|
539
465
|
* Register lazy module loaders by toolId.
|
|
540
466
|
* Toolbars call ensureToolModuleLoaded(toolId) before instance creation.
|
|
467
|
+
* A tool whose module has loaded, or is loading, keeps its loader: its
|
|
468
|
+
* elements are already defined, so a replacement could never take effect.
|
|
541
469
|
*/
|
|
542
470
|
setToolModuleLoaders(loaders) {
|
|
543
471
|
const entries = Object.entries(loaders).filter((entry) => entry[1] !== undefined);
|
|
@@ -551,6 +479,11 @@ export class ToolRegistry {
|
|
|
551
479
|
for (const [toolId, loader] of entries) {
|
|
552
480
|
if (this.moduleLoaders.get(toolId) === loader)
|
|
553
481
|
continue;
|
|
482
|
+
if (this.loadedToolModules.has(toolId) ||
|
|
483
|
+
this.moduleLoadPromises.has(toolId)) {
|
|
484
|
+
this.warnLoaderReplacedAfterLoad(toolId);
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
554
487
|
this.moduleLoaders.set(toolId, loader);
|
|
555
488
|
changedToolIds.push(toolId);
|
|
556
489
|
}
|
|
@@ -558,6 +491,12 @@ export class ToolRegistry {
|
|
|
558
491
|
this.emitChange({ kind: "module-loaders", toolIds: changedToolIds });
|
|
559
492
|
}
|
|
560
493
|
}
|
|
494
|
+
warnLoaderReplacedAfterLoad(toolId) {
|
|
495
|
+
if (this.warnedLoaderReplacements.has(toolId))
|
|
496
|
+
return;
|
|
497
|
+
this.warnedLoaderReplacements.add(toolId);
|
|
498
|
+
console.warn(`[ToolRegistry] Ignored a new module loader for "${toolId}": its module already loaded, so the elements it defines stay in place. Register loaders before the tool first renders.`);
|
|
499
|
+
}
|
|
561
500
|
/**
|
|
562
501
|
* Ensure tool module side-effects are loaded exactly once.
|
|
563
502
|
* Safe to call repeatedly; concurrent callers share the same promise.
|
|
@@ -571,11 +510,14 @@ export class ToolRegistry {
|
|
|
571
510
|
return;
|
|
572
511
|
}
|
|
573
512
|
const loader = this.moduleLoaders.get(toolId);
|
|
574
|
-
if (!loader)
|
|
513
|
+
if (!loader) {
|
|
514
|
+
this.watchUndefinedToolElement(toolId);
|
|
575
515
|
return;
|
|
516
|
+
}
|
|
576
517
|
const loadPromise = (async () => {
|
|
577
518
|
await loader();
|
|
578
519
|
this.loadedToolModules.add(toolId);
|
|
520
|
+
this.warnIfLoadedToolElementUndefined(toolId);
|
|
579
521
|
})();
|
|
580
522
|
this.moduleLoadPromises.set(toolId, loadPromise);
|
|
581
523
|
try {
|
|
@@ -586,10 +528,70 @@ export class ToolRegistry {
|
|
|
586
528
|
}
|
|
587
529
|
}
|
|
588
530
|
/**
|
|
589
|
-
*
|
|
531
|
+
* The undefined element tag a tool would render, or `null` when the tool is
|
|
532
|
+
* built by a component factory, its element is defined, or there is no DOM.
|
|
533
|
+
*/
|
|
534
|
+
undefinedToolElementTag(toolId) {
|
|
535
|
+
if (typeof customElements === "undefined" || typeof document === "undefined") {
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
538
|
+
const overrides = this.componentOverrides;
|
|
539
|
+
if (overrides.toolComponentFactories?.[toolId])
|
|
540
|
+
return null;
|
|
541
|
+
let tagName;
|
|
542
|
+
try {
|
|
543
|
+
tagName = resolveToolTag(toolId, overrides);
|
|
544
|
+
}
|
|
545
|
+
catch {
|
|
546
|
+
// Element creation reports the missing tag mapping.
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
return customElements.get(tagName) ? null : tagName;
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Warn when a tool's module loader finished without defining the element the
|
|
553
|
+
* tag map points the tool at, as when a host remaps the tag but keeps a
|
|
554
|
+
* loader that defines the packaged element. The tool otherwise renders blank.
|
|
555
|
+
*/
|
|
556
|
+
warnIfLoadedToolElementUndefined(toolId) {
|
|
557
|
+
const tagName = this.undefinedToolElementTag(toolId);
|
|
558
|
+
if (!tagName)
|
|
559
|
+
return;
|
|
560
|
+
warnOncePerDocument(document, `loadedToolElementUndefined.${toolId}.${tagName}`, `[ToolRegistry] Tool "${toolId}" renders <${tagName}>, but its module loader finished without defining it, so the tool renders blank. The tag map and the loader disagree: map "${toolId}" to the tag its loader defines, or register a loader that defines <${tagName}>. Reported once per page.`);
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Warn when a tool with no module loader still has no element definition
|
|
564
|
+
* after the pending-input delay. A registry built without loaders otherwise
|
|
565
|
+
* renders the tool as an unknown element with no error.
|
|
566
|
+
*/
|
|
567
|
+
watchUndefinedToolElement(toolId) {
|
|
568
|
+
if (this.watchedUndefinedToolElements.has(toolId))
|
|
569
|
+
return;
|
|
570
|
+
const tagName = this.undefinedToolElementTag(toolId);
|
|
571
|
+
if (!tagName)
|
|
572
|
+
return;
|
|
573
|
+
this.watchedUndefinedToolElements.add(toolId);
|
|
574
|
+
const doc = document;
|
|
575
|
+
const timer = setTimeout(() => {
|
|
576
|
+
if (customElements.get(tagName))
|
|
577
|
+
return;
|
|
578
|
+
warnOncePerDocument(doc, `undefinedToolElement.${toolId}`, `[ToolRegistry] Tool "${toolId}" renders <${tagName}>, which is still undefined after ${PENDING_INPUT_WARNING_DELAY_MS / 1000} s, and its registry has no module loader for it. Pass toolModuleLoaders to createPackagedToolRegistry (DEFAULT_TOOL_MODULE_LOADERS from @pie-players/pie-default-tool-loaders loads the stock tools), register one with setToolModuleLoaders, or import the tool's package before it renders. Reported once per page.`);
|
|
579
|
+
}, PENDING_INPUT_WARNING_DELAY_MS);
|
|
580
|
+
void customElements.whenDefined(tagName).then(() => clearTimeout(timer));
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Load a set of tool modules, each to completion, and resolve with the error
|
|
584
|
+
* of every tool whose module failed, by tool id. One failure leaves the other
|
|
585
|
+
* tools loaded and usable. A failed tool is retried on the next call.
|
|
590
586
|
*/
|
|
591
587
|
async ensureToolModulesLoaded(toolIds) {
|
|
592
|
-
await Promise.
|
|
588
|
+
const results = await Promise.allSettled(toolIds.map((toolId) => this.ensureToolModuleLoaded(toolId)));
|
|
589
|
+
const failures = new Map();
|
|
590
|
+
results.forEach((result, index) => {
|
|
591
|
+
if (result.status === "rejected")
|
|
592
|
+
failures.set(toolIds[index], result.reason);
|
|
593
|
+
});
|
|
594
|
+
return failures;
|
|
593
595
|
}
|
|
594
596
|
/**
|
|
595
597
|
* Whether a tool module has already been loaded.
|