@pie-players/pie-assessment-toolkit 0.3.74 → 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +658 -714
- package/custom-elements.json +1315 -0
- package/dist/attempt/TestSession.d.ts +1 -21
- package/dist/attempt/TestSession.js +16 -102
- 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.js +1 -1
- package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
- package/dist/components/SectionToolBar.custom-element.js +1 -1
- package/dist/components/chunks/ItemToolBar-3zrc4p3d.js +4 -0
- package/dist/components/chunks/ItemToolBar-66hcwmfw.js +2 -0
- package/dist/components/chunks/ItemToolBar-9gqhfxtn.js +40 -0
- package/dist/components/chunks/ItemToolBar-hzc5wyjp.js +2 -0
- package/dist/components/chunks/{ItemToolBar-gmrryv2v.js → ItemToolBar-kkj69ptr.js} +1 -1
- package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
- package/dist/components/chunks/sre-engine-bj997w4b.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 +33 -6
- package/dist/index.d.ts +25 -50
- package/dist/index.js +15 -45
- package/dist/policy/core/PolicySource.d.ts +2 -2
- package/dist/policy/core/ToolPolicyEngine.d.ts +118 -34
- package/dist/policy/core/ToolPolicyEngine.js +203 -56
- package/dist/policy/core/compose-decision.d.ts +28 -7
- package/dist/policy/core/compose-decision.js +95 -74
- package/dist/policy/core/decision-types.d.ts +98 -55
- package/dist/policy/core/feature-decision.d.ts +28 -19
- package/dist/policy/core/feature-decision.js +15 -11
- package/dist/policy/core/pnp-policy-inputs.d.ts +29 -35
- package/dist/policy/core/pnp-policy-inputs.js +40 -88
- package/dist/policy/core/provenance.d.ts +10 -5
- package/dist/policy/core/provenance.js +1 -1
- package/dist/policy/core/tool-parameters.d.ts +11 -0
- package/dist/policy/core/tool-parameters.js +15 -0
- package/dist/policy/engine.d.ts +7 -23
- package/dist/policy/engine.js +5 -20
- package/dist/policy/sources/PnpPolicySource.d.ts +43 -68
- package/dist/policy/sources/PnpPolicySource.js +133 -132
- package/dist/runtime/SectionControllerBinding.d.ts +283 -0
- package/dist/runtime/SectionControllerBinding.js +456 -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 -3
- package/dist/runtime/adapter/subscriber-fanout.js +2 -6
- 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 +12 -18
- package/dist/runtime/composition-emit-scheduler.js +10 -16
- package/dist/runtime/content-language.d.ts +32 -0
- package/dist/runtime/content-language.js +46 -0
- package/dist/runtime/content-region.d.ts +13 -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 -4
- package/dist/runtime/core/SectionEngineCore.js +2 -6
- package/dist/runtime/core/cohort.d.ts +1 -6
- package/dist/runtime/core/cohort.js +1 -10
- 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 +17 -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 +74 -53
- 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 +22 -14
- package/dist/runtime/runtime-event-claim.d.ts +8 -0
- package/dist/runtime/runtime-event-claim.js +15 -0
- package/dist/runtime/runtime-id.d.ts +1 -1
- package/dist/runtime/runtime-id.js +1 -1
- 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 +194 -0
- package/dist/runtime/tool-host-contract.d.ts +8 -14
- package/dist/runtime/tool-host-contract.js +12 -12
- package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
- package/dist/runtime/unclaimed-registration-watch.js +23 -0
- package/dist/services/AccessibilityCatalogResolver.d.ts +21 -31
- package/dist/services/AccessibilityCatalogResolver.js +29 -42
- package/dist/services/ElementToolStateStore.d.ts +42 -67
- package/dist/services/ElementToolStateStore.js +65 -71
- package/dist/services/HighlightCoordinator.d.ts +28 -40
- package/dist/services/HighlightCoordinator.js +459 -381
- package/dist/services/RangeSerializer.d.ts +11 -33
- package/dist/services/RangeSerializer.js +88 -59
- package/dist/services/SSMLExtractor.d.ts +8 -31
- package/dist/services/SSMLExtractor.js +18 -40
- package/dist/services/TTSService.d.ts +159 -119
- package/dist/services/TTSService.js +937 -822
- package/dist/services/ToolCoordinator.d.ts +42 -102
- package/dist/services/ToolCoordinator.js +178 -256
- package/dist/services/ToolRegistry.d.ts +152 -133
- package/dist/services/ToolRegistry.js +192 -178
- package/dist/services/ToolkitCoordinator.d.ts +233 -237
- package/dist/services/ToolkitCoordinator.js +615 -557
- package/dist/services/audio-handoff.d.ts +6 -7
- package/dist/services/audio-handoff.js +20 -14
- package/dist/services/catalog-owner.d.ts +0 -1
- 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 +51 -13
- package/dist/services/framework-error.js +19 -20
- package/dist/services/interfaces.d.ts +189 -184
- package/dist/services/section-controller-types.d.ts +7 -7
- package/dist/services/section-session-assignment.d.ts +21 -0
- package/dist/services/section-session-assignment.js +80 -0
- package/dist/services/spoken-audio-cards.d.ts +2 -6
- package/dist/services/spoken-audio-cards.js +2 -6
- 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 +9 -12
- package/dist/services/tool-config-validation.js +47 -87
- package/dist/services/tool-context.d.ts +7 -31
- package/dist/services/tool-context.js +99 -65
- package/dist/services/tool-failure.d.ts +30 -0
- package/dist/services/tool-failure.js +11 -0
- package/dist/services/tool-icons.d.ts +5 -5
- package/dist/services/tool-icons.js +6 -5
- package/dist/services/tool-instance-id.d.ts +8 -7
- package/dist/services/tool-instance-id.js +12 -24
- package/dist/services/tool-providers/TTSToolProvider.d.ts +20 -58
- package/dist/services/tool-providers/TTSToolProvider.js +32 -85
- package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -11
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts +29 -60
- package/dist/services/tool-providers/ToolProviderRegistry.js +128 -144
- package/dist/services/tool-request.d.ts +31 -12
- package/dist/services/tool-request.js +31 -16
- package/dist/services/toolbar-decided-tools.d.ts +16 -0
- package/dist/services/toolbar-decided-tools.js +29 -0
- package/dist/services/toolbar-items.js +2 -5
- package/dist/services/tools-config-normalizer.d.ts +33 -29
- package/dist/services/tools-config-normalizer.js +26 -28
- package/dist/services/tts/browser-provider.d.ts +39 -6
- package/dist/services/tts/browser-provider.js +280 -160
- package/dist/services/tts/catalog-span-alignment.js +14 -7
- package/dist/services/tts/flat-tree.d.ts +64 -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/dom/to-chunk-inputs.d.ts +2 -2
- package/dist/services/tts/generated-speech/index.d.ts +1 -1
- package/dist/services/tts/generated-speech/index.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 +1 -1
- package/dist/services/tts/generated-speech/types.js +1 -1
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +22 -16
- 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/highlight-target-resolver.d.ts +12 -0
- package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
- package/dist/services/tts/math-aware-text-processing.js +42 -47
- 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 +12 -1
- package/dist/services/tts/math-speech.js +54 -18
- package/dist/services/tts/mathml-sanitization.d.ts +7 -0
- package/dist/services/tts/mathml-sanitization.js +9 -1
- package/dist/services/tts/provider-options.d.ts +52 -0
- package/dist/services/tts/provider-options.js +1 -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 +4 -0
- package/dist/services/tts/start-failure.js +19 -0
- package/dist/services/tts/text-processing.d.ts +9 -0
- package/dist/services/tts/text-processing.js +46 -33
- package/dist/services/tts/text-segmentation.d.ts +12 -0
- package/dist/services/tts/text-segmentation.js +60 -10
- package/dist/services/tts-runtime-config.d.ts +54 -26
- package/dist/services/tts-runtime-config.js +27 -112
- package/dist/tools/content-capability-resolution.d.ts +9 -11
- package/dist/tools/content-capability-resolution.js +8 -45
- package/dist/tools/registration.d.ts +59 -0
- package/dist/tools/registration.js +55 -0
- package/dist/tools/registrations/toolbar-registration-helpers.d.ts +6 -0
- package/dist/tools/registrations/toolbar-registration-helpers.js +8 -2
- package/dist/tools/tool-surface-host.d.ts +6 -10
- package/dist/tools/tool-surface-host.js +45 -50
- 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 +15 -29
- 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-b9dz5jvd.js +0 -40
- package/dist/components/chunks/ItemToolBar-h4xap0t0.js +0 -4
- package/dist/components/chunks/ItemToolBar-wr2myaah.js +0 -2
- package/dist/components/chunks/sre-engine-wf9s43gg.js +0 -1
- package/dist/context/runtime-context-consumer.d.ts +0 -13
- package/dist/context/runtime-context-consumer.js +0 -18
- 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 -74
- package/dist/runtime/section-runtime-engine-host-context.js +0 -57
- package/dist/runtime/stage-emit-gate.d.ts +0 -92
- package/dist/runtime/stage-emit-gate.js +0 -82
- package/dist/services/ContextVariableStore.d.ts +0 -113
- package/dist/services/ContextVariableStore.js +0 -206
- 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/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/tts-control-events.d.ts +0 -13
- package/dist/services/tts-control-events.js +0 -7
- package/dist/tools/client.d.ts +0 -10
- package/dist/tools/client.js +0 -10
- package/dist/tools/internal.d.ts +0 -44
- package/dist/tools/internal.js +0 -39
- package/dist/utils/logger.d.ts +0 -6
- package/dist/utils/logger.js +0 -10
|
@@ -3,24 +3,24 @@
|
|
|
3
3
|
* `docs/tools-and-accomodations/architecture.md`.
|
|
4
4
|
*
|
|
5
5
|
* Wraps `composeDecision(...)` with engine-instance state:
|
|
6
|
-
* - bound inputs (`tools`, `assessment`, `
|
|
6
|
+
* - bound inputs (`tools`, `assessment`, `pnpEnforcement`) and the
|
|
7
|
+
* settings of the mounted items
|
|
7
8
|
* - registered custom `PolicySource`s
|
|
8
9
|
* - `onPolicyChange` subscriber bus
|
|
9
10
|
* - `dispose()` clean-up
|
|
10
11
|
*
|
|
11
12
|
* The class has no DOM dependency and no Svelte runes. It is safe to
|
|
12
13
|
* instantiate inside a worker, in a Node test, or inside a CE
|
|
13
|
-
* connectedCallback.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* PR 1 ships the engine *without callers*. PR 2 wires it into
|
|
17
|
-
* `ToolkitCoordinator`. PR 3 switches `<pie-item-toolbar>` over.
|
|
14
|
+
* connectedCallback. `ToolkitCoordinator` owns the instance, and
|
|
15
|
+
* `<pie-item-toolbar>` reads its decisions through the coordinator.
|
|
18
16
|
*/
|
|
19
17
|
import { normalizeToolList, normalizeToolsConfig, } from "../../services/tools-config-normalizer.js";
|
|
20
18
|
import { hostFeatureDenial, interpretFeatureResult, } from "./feature-decision.js";
|
|
21
|
-
import { composeDecision } from "./compose-decision.js";
|
|
19
|
+
import { composeDecision, unknownSupportIdDiagnostic, } from "./compose-decision.js";
|
|
22
20
|
import { resolveDefaultPnpEnforcement } from "./pnp-policy-inputs.js";
|
|
23
|
-
import {
|
|
21
|
+
import { resolveToolParameters } from "./tool-parameters.js";
|
|
22
|
+
import { structurallyEqual } from "../../utils/structural-equality.js";
|
|
23
|
+
import { PnpPolicySource, } from "../sources/PnpPolicySource.js";
|
|
24
24
|
const DEFAULT_TOOLS = normalizeToolsConfig({
|
|
25
25
|
policy: { allowed: [], blocked: [] },
|
|
26
26
|
placement: { section: [], item: [], passage: [] },
|
|
@@ -28,30 +28,34 @@ const DEFAULT_TOOLS = normalizeToolsConfig({
|
|
|
28
28
|
});
|
|
29
29
|
export class ToolPolicyEngine {
|
|
30
30
|
toolRegistry;
|
|
31
|
+
unsubscribeRegistry;
|
|
31
32
|
contextId;
|
|
33
|
+
assessmentOptional;
|
|
32
34
|
pnpPolicySource;
|
|
33
35
|
customSources;
|
|
34
36
|
listeners = new Set();
|
|
35
37
|
tools;
|
|
36
38
|
assessment;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
pnpEnforcementOverride;
|
|
40
|
+
/**
|
|
41
|
+
* Mounted items' settings by canonical item id, newest registration last;
|
|
42
|
+
* see {@link registerItemSettings}.
|
|
43
|
+
*/
|
|
44
|
+
itemSettings = new Map();
|
|
45
|
+
inputDiagnostics = Object.freeze([]);
|
|
39
46
|
disposed = false;
|
|
40
47
|
constructor(args) {
|
|
41
48
|
this.toolRegistry = args.toolRegistry;
|
|
42
49
|
this.contextId = args.contextId ?? "tool-policy";
|
|
50
|
+
this.assessmentOptional = args.assessmentOptional === true;
|
|
43
51
|
this.pnpPolicySource = new PnpPolicySource(this.toolRegistry);
|
|
44
52
|
this.customSources = args.customSources ? [...args.customSources] : [];
|
|
45
53
|
const inputs = args.inputs ?? {};
|
|
46
54
|
this.tools = inputs.tools ?? DEFAULT_TOOLS;
|
|
47
55
|
this.assessment = inputs.assessment ?? null;
|
|
48
|
-
this.
|
|
49
|
-
this.
|
|
50
|
-
|
|
51
|
-
resolveDefaultPnpEnforcement({
|
|
52
|
-
assessment: this.assessment,
|
|
53
|
-
currentItemRef: this.currentItemRef,
|
|
54
|
-
});
|
|
56
|
+
this.pnpEnforcementOverride = inputs.pnpEnforcement ?? null;
|
|
57
|
+
this.refreshInputDiagnostics();
|
|
58
|
+
this.unsubscribeRegistry = this.watchRegistry();
|
|
55
59
|
}
|
|
56
60
|
/**
|
|
57
61
|
* Resolve the visible tool set for a given placement level and
|
|
@@ -67,27 +71,92 @@ export class ToolPolicyEngine {
|
|
|
67
71
|
* values even though the policy decision itself is identical.
|
|
68
72
|
* Hosts that want timestamp-stable provenance should reuse a
|
|
69
73
|
* cached `ToolPolicyDecision` rather than re-call `decide(...)`.
|
|
74
|
+
*
|
|
75
|
+
* An item's registered settings apply to the item's own toolbar: a request
|
|
76
|
+
* at item level whose scope is the item. Every other request — a section,
|
|
77
|
+
* assessment or passage toolbar — is shared by the items it sits beside, so
|
|
78
|
+
* it leaves every item's settings out and reports each tool on its toolbar
|
|
79
|
+
* that one restricts or requires as `tool-policy.itemSettingNotApplied`.
|
|
70
80
|
*/
|
|
71
81
|
decide(request) {
|
|
72
82
|
this.assertNotDisposed();
|
|
73
83
|
const requestContextId = request.scope.scopeId
|
|
74
84
|
? `${this.contextId}:${request.level}:${request.scope.scopeId}`
|
|
75
85
|
: `${this.contextId}:${request.level}`;
|
|
86
|
+
const item = request.level === "item" ? this.itemForScope(request.scope) : undefined;
|
|
87
|
+
const shared = request.level !== "item" ||
|
|
88
|
+
request.scope.level === "section" ||
|
|
89
|
+
request.scope.level === "assessment";
|
|
76
90
|
return composeDecision({
|
|
77
91
|
request,
|
|
78
92
|
tools: this.tools,
|
|
79
93
|
pnpPolicy: {
|
|
80
94
|
source: this.pnpPolicySource,
|
|
81
95
|
assessment: this.assessment ?? undefined,
|
|
82
|
-
|
|
83
|
-
enforcement: this.
|
|
96
|
+
item,
|
|
97
|
+
enforcement: this.enforcementFor(item),
|
|
84
98
|
},
|
|
99
|
+
unappliedItems: shared ? this.enforcedItems() : undefined,
|
|
85
100
|
customSources: this.customSources,
|
|
86
101
|
contextId: requestContextId,
|
|
87
102
|
});
|
|
88
103
|
}
|
|
89
104
|
/**
|
|
90
|
-
*
|
|
105
|
+
* Whether some surface is granted `featureId`: a decision with no item scope,
|
|
106
|
+
* or one scoped to a registered item. A provider serves every surface, so its
|
|
107
|
+
* failure denies the accommodation wherever one surface is granted it.
|
|
108
|
+
*
|
|
109
|
+
* `enforced` counts only scopes whose `pnpEnforcement` is on, as a toolbar
|
|
110
|
+
* tool's decisions do; a feature with no placement leaves it unset.
|
|
111
|
+
*/
|
|
112
|
+
grantsFeatureAnywhere(featureId, options = {}) {
|
|
113
|
+
const counts = (item) => !options.enforced || this.enforcementFor(item) === "on";
|
|
114
|
+
if (counts() && this.decideFeature(featureId).granted)
|
|
115
|
+
return true;
|
|
116
|
+
for (const id of this.itemSettings.keys()) {
|
|
117
|
+
const settings = this.itemSettingsFor(id);
|
|
118
|
+
if (!counts(settings ? { id, settings } : undefined))
|
|
119
|
+
continue;
|
|
120
|
+
if (this.decideFeature(featureId, { level: "item", scopeId: id }).granted) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* File a mounted item's settings under its canonical id. They govern the
|
|
128
|
+
* decisions scoped to that item: its own toolbar, and a feature its content
|
|
129
|
+
* asks about with the item's scope. See {@link decide}.
|
|
130
|
+
*
|
|
131
|
+
* The same item mounted twice registers twice; the newest registration
|
|
132
|
+
* applies, and its cleanup hands the item back to the one before. Emits an
|
|
133
|
+
* `"item-settings"` change whenever the settings that apply to the item
|
|
134
|
+
* change.
|
|
135
|
+
*/
|
|
136
|
+
registerItemSettings(itemId, settings) {
|
|
137
|
+
this.assertNotDisposed();
|
|
138
|
+
const entry = { settings };
|
|
139
|
+
const before = this.itemSettingsFor(itemId);
|
|
140
|
+
const entries = this.itemSettings.get(itemId) ?? [];
|
|
141
|
+
entries.push(entry);
|
|
142
|
+
this.itemSettings.set(itemId, entries);
|
|
143
|
+
this.emitIfItemSettingsChanged(itemId, before);
|
|
144
|
+
return () => {
|
|
145
|
+
if (this.disposed)
|
|
146
|
+
return;
|
|
147
|
+
const current = this.itemSettings.get(itemId);
|
|
148
|
+
const index = current?.indexOf(entry) ?? -1;
|
|
149
|
+
if (!current || index < 0)
|
|
150
|
+
return;
|
|
151
|
+
const previous = this.itemSettingsFor(itemId);
|
|
152
|
+
current.splice(index, 1);
|
|
153
|
+
if (current.length === 0)
|
|
154
|
+
this.itemSettings.delete(itemId);
|
|
155
|
+
this.emitIfItemSettingsChanged(itemId, previous);
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Resolve eligibility for one PNP/AfA feature id through the eight-level
|
|
91
160
|
* precedence, independent of toolbar placement.
|
|
92
161
|
*
|
|
93
162
|
* For capabilities that render as their own surface rather than a toolbar
|
|
@@ -108,20 +177,24 @@ export class ToolPolicyEngine {
|
|
|
108
177
|
* The decision reports whether an assessment was bound, which is the engine's
|
|
109
178
|
* to answer rather than the policy source's — see
|
|
110
179
|
* {@link FeaturePolicyDecision.assessmentBound}.
|
|
180
|
+
*
|
|
181
|
+
* `scope` is the surface asking. An item's scope brings in the item's
|
|
182
|
+
* registered settings, as {@link decide} does for the item's toolbar.
|
|
111
183
|
*/
|
|
112
|
-
decideFeature(featureId) {
|
|
184
|
+
decideFeature(featureId, scope) {
|
|
113
185
|
this.assertNotDisposed();
|
|
114
186
|
const hostDenial = this.hostFeatureGate(featureId);
|
|
115
187
|
if (hostDenial)
|
|
116
188
|
return hostDenial;
|
|
189
|
+
const item = this.itemForScope(scope);
|
|
117
190
|
return interpretFeatureResult(featureId, this.pnpPolicySource.resolveFeature(featureId, {
|
|
118
191
|
assessment: this.assessment ?? undefined,
|
|
119
|
-
|
|
192
|
+
item,
|
|
120
193
|
}),
|
|
121
194
|
// `resolveFeature` takes the assessment as `undefined` either way, so the
|
|
122
195
|
// source cannot tell an unbound host from one whose profile is silent.
|
|
123
196
|
// The engine can.
|
|
124
|
-
{ assessmentBound: this.assessment !== null });
|
|
197
|
+
{ assessmentBound: this.assessment !== null }, resolveToolParameters(featureId, this.assessment, item));
|
|
125
198
|
}
|
|
126
199
|
/**
|
|
127
200
|
* The host gates that hold for a feature id, or `null` when none fires.
|
|
@@ -159,17 +232,14 @@ export class ToolPolicyEngine {
|
|
|
159
232
|
/**
|
|
160
233
|
* Apply a partial input update.
|
|
161
234
|
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
* `PieAssessmentToolkit.svelte`'s prop-forwarding effect and by the
|
|
167
|
-
* coordinator's `decideToolPolicy()` tests.
|
|
235
|
+
* A key that did not change does not fire `onPolicyChange` listeners.
|
|
236
|
+
* `tools` is diffed with `Object.is`. `assessment` is diffed structurally,
|
|
237
|
+
* because hosts rebuild it from their own state on every render; the latest
|
|
238
|
+
* reference is stored either way.
|
|
168
239
|
*
|
|
169
|
-
* `pnpEnforcement`
|
|
170
|
-
* a distinct `"pnp-enforcement"` reason when no
|
|
171
|
-
*
|
|
172
|
-
* event channel.
|
|
240
|
+
* `pnpEnforcement` sets the override, `null` or `undefined` returning to
|
|
241
|
+
* auto-mode, and emits with a distinct `"pnp-enforcement"` reason when no
|
|
242
|
+
* other key changed.
|
|
173
243
|
*/
|
|
174
244
|
updateInputs(patch) {
|
|
175
245
|
this.assertNotDisposed();
|
|
@@ -184,21 +254,17 @@ export class ToolPolicyEngine {
|
|
|
184
254
|
}
|
|
185
255
|
if ("assessment" in patch) {
|
|
186
256
|
const next = patch.assessment ?? null;
|
|
187
|
-
|
|
188
|
-
|
|
257
|
+
const assessmentChanged = !structurallyEqual(this.assessment, next);
|
|
258
|
+
this.assessment = next;
|
|
259
|
+
if (assessmentChanged) {
|
|
189
260
|
changed = true;
|
|
261
|
+
this.refreshInputDiagnostics();
|
|
190
262
|
}
|
|
191
263
|
}
|
|
192
|
-
if ("
|
|
193
|
-
const next = patch.
|
|
194
|
-
if (
|
|
195
|
-
this.
|
|
196
|
-
changed = true;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
if ("pnpEnforcement" in patch && patch.pnpEnforcement) {
|
|
200
|
-
if (this.pnpEnforcement !== patch.pnpEnforcement) {
|
|
201
|
-
this.pnpEnforcement = patch.pnpEnforcement;
|
|
264
|
+
if ("pnpEnforcement" in patch) {
|
|
265
|
+
const next = patch.pnpEnforcement ?? null;
|
|
266
|
+
if (this.pnpEnforcementOverride !== next) {
|
|
267
|
+
this.pnpEnforcementOverride = next;
|
|
202
268
|
pnpChanged = true;
|
|
203
269
|
}
|
|
204
270
|
}
|
|
@@ -216,9 +282,12 @@ export class ToolPolicyEngine {
|
|
|
216
282
|
*/
|
|
217
283
|
replaceToolRegistry(toolRegistry, tools) {
|
|
218
284
|
this.assertNotDisposed();
|
|
285
|
+
this.unsubscribeRegistry();
|
|
219
286
|
this.toolRegistry = toolRegistry;
|
|
220
287
|
this.pnpPolicySource = new PnpPolicySource(toolRegistry);
|
|
288
|
+
this.unsubscribeRegistry = this.watchRegistry();
|
|
221
289
|
this.tools = tools;
|
|
290
|
+
this.refreshInputDiagnostics();
|
|
222
291
|
this.emit({ reason: "inputs", inputs: this.snapshotInputs() });
|
|
223
292
|
}
|
|
224
293
|
registerPolicySource(source) {
|
|
@@ -230,15 +299,15 @@ export class ToolPolicyEngine {
|
|
|
230
299
|
});
|
|
231
300
|
return () => {
|
|
232
301
|
const idx = this.customSources.indexOf(source);
|
|
233
|
-
if (idx
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
302
|
+
if (idx === -1)
|
|
303
|
+
return;
|
|
304
|
+
this.customSources.splice(idx, 1);
|
|
305
|
+
if (this.disposed)
|
|
306
|
+
return;
|
|
307
|
+
this.emit({
|
|
308
|
+
reason: "policy-source-removed",
|
|
309
|
+
inputs: this.snapshotInputs(),
|
|
310
|
+
});
|
|
242
311
|
};
|
|
243
312
|
}
|
|
244
313
|
onPolicyChange(listener) {
|
|
@@ -254,21 +323,99 @@ export class ToolPolicyEngine {
|
|
|
254
323
|
if (this.disposed)
|
|
255
324
|
return;
|
|
256
325
|
this.disposed = true;
|
|
326
|
+
this.unsubscribeRegistry();
|
|
257
327
|
this.emit({
|
|
258
328
|
reason: "disposed",
|
|
259
329
|
inputs: this.snapshotInputs(),
|
|
260
330
|
});
|
|
261
331
|
this.listeners.clear();
|
|
332
|
+
this.itemSettings.clear();
|
|
262
333
|
this.customSources.length = 0;
|
|
263
334
|
}
|
|
264
335
|
snapshotInputs() {
|
|
265
336
|
return Object.freeze({
|
|
266
337
|
tools: this.tools,
|
|
267
338
|
assessment: this.assessment,
|
|
268
|
-
|
|
269
|
-
|
|
339
|
+
pnpEnforcement: this.enforcementFor(),
|
|
340
|
+
pnpEnforcementOverride: this.pnpEnforcementOverride,
|
|
341
|
+
assessmentExpected: !this.assessmentOptional || this.pnpEnforcementOverride === "on",
|
|
342
|
+
diagnostics: this.inputDiagnostics,
|
|
270
343
|
});
|
|
271
344
|
}
|
|
345
|
+
/** The override, else auto-mode over the assessment and `item`'s settings. */
|
|
346
|
+
enforcementFor(item) {
|
|
347
|
+
return (this.pnpEnforcementOverride ??
|
|
348
|
+
resolveDefaultPnpEnforcement({
|
|
349
|
+
assessment: this.assessment,
|
|
350
|
+
itemSettings: item?.settings,
|
|
351
|
+
}));
|
|
352
|
+
}
|
|
353
|
+
/** The settings that apply to `itemId`: its newest registration's. */
|
|
354
|
+
itemSettingsFor(itemId) {
|
|
355
|
+
return this.itemSettings.get(itemId)?.at(-1)?.settings;
|
|
356
|
+
}
|
|
357
|
+
emitIfItemSettingsChanged(itemId, before) {
|
|
358
|
+
if (structurallyEqual(before, this.itemSettingsFor(itemId)))
|
|
359
|
+
return;
|
|
360
|
+
this.refreshInputDiagnostics();
|
|
361
|
+
this.emit({ reason: "item-settings", inputs: this.snapshotInputs() });
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Recompute {@link ResolvedEngineInputs.diagnostics} from the bound
|
|
365
|
+
* assessment, every mounted item's settings and the registry. Returns
|
|
366
|
+
* whether they changed.
|
|
367
|
+
*/
|
|
368
|
+
refreshInputDiagnostics() {
|
|
369
|
+
const items = [];
|
|
370
|
+
for (const id of this.itemSettings.keys()) {
|
|
371
|
+
const settings = this.itemSettingsFor(id);
|
|
372
|
+
if (settings)
|
|
373
|
+
items.push(settings);
|
|
374
|
+
}
|
|
375
|
+
const next = Array.from(this.pnpPolicySource.unknownSupportIds(this.assessment, items), ([supportId, origins]) => unknownSupportIdDiagnostic(supportId, origins));
|
|
376
|
+
if (structurallyEqual(this.inputDiagnostics, next))
|
|
377
|
+
return false;
|
|
378
|
+
this.inputDiagnostics = Object.freeze(next);
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Registering a tool into the bound registry, or removing one, can make a
|
|
383
|
+
* named id known or unknown, so it emits an `"inputs"` change when the input
|
|
384
|
+
* diagnostics move.
|
|
385
|
+
*/
|
|
386
|
+
watchRegistry() {
|
|
387
|
+
return this.toolRegistry.onRegistryChange(() => {
|
|
388
|
+
if (this.disposed)
|
|
389
|
+
return;
|
|
390
|
+
if (this.refreshInputDiagnostics()) {
|
|
391
|
+
this.emit({ reason: "inputs", inputs: this.snapshotInputs() });
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* The registered item an item scope names, by its canonical id. A scope
|
|
397
|
+
* of any other level names none.
|
|
398
|
+
*/
|
|
399
|
+
itemForScope(scope) {
|
|
400
|
+
if (scope?.level !== "item")
|
|
401
|
+
return undefined;
|
|
402
|
+
const id = scope.canonicalItemId || scope.itemId || scope.scopeId;
|
|
403
|
+
const settings = id ? this.itemSettingsFor(id) : undefined;
|
|
404
|
+
return settings ? { id, settings } : undefined;
|
|
405
|
+
}
|
|
406
|
+
/** The registered items whose settings their own toolbar enforces. */
|
|
407
|
+
enforcedItems() {
|
|
408
|
+
const items = [];
|
|
409
|
+
for (const id of this.itemSettings.keys()) {
|
|
410
|
+
const settings = this.itemSettingsFor(id);
|
|
411
|
+
if (!settings)
|
|
412
|
+
continue;
|
|
413
|
+
const item = { id, settings };
|
|
414
|
+
if (this.enforcementFor(item) === "on")
|
|
415
|
+
items.push(item);
|
|
416
|
+
}
|
|
417
|
+
return items;
|
|
418
|
+
}
|
|
272
419
|
emit(event) {
|
|
273
420
|
for (const listener of Array.from(this.listeners)) {
|
|
274
421
|
try {
|
|
@@ -11,27 +11,48 @@
|
|
|
11
11
|
* The tests in
|
|
12
12
|
* `tests/policy/compose-decision.test.ts` lock the orchestration in.
|
|
13
13
|
*/
|
|
14
|
-
import type { AssessmentEntity
|
|
15
|
-
import { type CanonicalToolsConfig } from "../../services/tools-config-normalizer.js";
|
|
16
|
-
import type { ToolPolicyDecision, ToolPolicyDecisionRequest } from "./decision-types.js";
|
|
14
|
+
import type { AssessmentEntity } from "@pie-players/pie-players-shared/types";
|
|
15
|
+
import { type CanonicalToolsConfig, type ToolPlacementLevel } from "../../services/tools-config-normalizer.js";
|
|
16
|
+
import type { OverrideBlockedDetails, ToolPolicyDecision, ToolPolicyDecisionRequest, ToolPolicyDiagnostic } from "./decision-types.js";
|
|
17
17
|
import type { PolicySource } from "./PolicySource.js";
|
|
18
|
-
import type
|
|
18
|
+
import { type PnpPolicySourceRule } from "./provenance.js";
|
|
19
|
+
import type { PnpPolicyItem, PnpPolicySource } from "../sources/PnpPolicySource.js";
|
|
19
20
|
export interface ComposeDecisionInputs {
|
|
20
21
|
request: ToolPolicyDecisionRequest;
|
|
21
22
|
tools: CanonicalToolsConfig;
|
|
22
23
|
pnpPolicy: {
|
|
23
24
|
source: PnpPolicySource | null;
|
|
24
25
|
assessment?: AssessmentEntity;
|
|
25
|
-
|
|
26
|
+
/** The item an item-scoped decision is for; see {@link PnpPolicyItem}. */
|
|
27
|
+
item?: PnpPolicyItem;
|
|
26
28
|
/**
|
|
27
29
|
* "on" applies the PNP/profile precedence rules; "off" skips step 5
|
|
28
|
-
* entirely.
|
|
29
|
-
*
|
|
30
|
+
* entirely. The engine resolves it from the enforcement override, or
|
|
31
|
+
* from whether the assessment or item carries PNP/profile material.
|
|
30
32
|
*/
|
|
31
33
|
enforcement: "on" | "off";
|
|
32
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Mounted items whose settings this decision leaves out: set for a section-
|
|
37
|
+
* or assessment-level toolbar. A tool on that toolbar that one of them
|
|
38
|
+
* restricts or requires gets a `tool-policy.itemSettingNotApplied`
|
|
39
|
+
* diagnostic.
|
|
40
|
+
*/
|
|
41
|
+
unappliedItems?: readonly PnpPolicyItem[];
|
|
33
42
|
customSources: readonly PolicySource[];
|
|
34
43
|
/** Stable identifier used as the provenance `contextId`. */
|
|
35
44
|
contextId: string;
|
|
36
45
|
}
|
|
37
46
|
export declare function composeDecision(inputs: ComposeDecisionInputs): ToolPolicyDecision;
|
|
47
|
+
/**
|
|
48
|
+
* A `tool-policy.overrideBlocked` diagnostic for a tool a `true` test
|
|
49
|
+
* administration override grants and `rule` withdraws. `level` is absent on a
|
|
50
|
+
* feature decision, which has no toolbar level.
|
|
51
|
+
*/
|
|
52
|
+
export declare function overrideBlockedDiagnostic(toolId: string, rule: OverrideBlockedDetails["rule"], level?: ToolPlacementLevel): ToolPolicyDiagnostic;
|
|
53
|
+
/**
|
|
54
|
+
* A `tool-policy.unknownSupportId` diagnostic for an id the registry lacks,
|
|
55
|
+
* naming every list that names it. It describes the engine's inputs rather
|
|
56
|
+
* than a decision, so it carries no level.
|
|
57
|
+
*/
|
|
58
|
+
export declare function unknownSupportIdDiagnostic(supportId: string, origins: readonly PnpPolicySourceRule[]): ToolPolicyDiagnostic;
|