@pie-players/pie-players-shared 1.0.0-beta.7 → 1.0.0-beta.9
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 +16 -14
- package/dist/i18n/catalogs.d.ts +20 -13
- package/dist/i18n/catalogs.js +22 -23
- package/dist/i18n/language-tags.d.ts +23 -6
- package/dist/i18n/language-tags.js +74 -9
- package/dist/i18n/messages/en-US.d.ts +21 -4
- package/dist/i18n/messages/en-US.js +23 -4
- package/dist/i18n/types.d.ts +7 -2
- package/dist/index.d.ts +1 -1
- package/dist/item-session-control/index.d.ts +16 -0
- package/dist/item-session-control/index.js +5 -0
- package/dist/{formative → item-session-control}/mastery.d.ts +5 -5
- package/dist/{formative → item-session-control}/mastery.js +3 -3
- package/dist/{formative → item-session-control}/outcome.d.ts +3 -3
- package/dist/{formative → item-session-control}/outcome.js +2 -2
- package/dist/{formative → item-session-control}/policy.d.ts +11 -11
- package/dist/{formative → item-session-control}/policy.js +12 -12
- package/dist/item-session-control/session.d.ts +19 -0
- package/dist/{formative → item-session-control}/session.js +12 -12
- package/dist/{formative → item-session-control}/state.d.ts +26 -26
- package/dist/{formative → item-session-control}/state.js +13 -13
- package/dist/{formative → item-session-control}/types.d.ts +38 -40
- package/dist/{formative → item-session-control}/types.js +2 -2
- package/dist/security/private-hosts.d.ts +27 -0
- package/dist/security/private-hosts.js +85 -0
- package/dist/timed-media/index.d.ts +1 -1
- package/dist/timed-media/index.js +1 -1
- package/dist/timed-media/policy.d.ts +2 -2
- package/dist/timed-media/policy.js +2 -2
- package/dist/timed-media/session.d.ts +1 -1
- package/dist/timed-media/session.js +1 -1
- package/dist/timed-media/types.d.ts +11 -11
- package/dist/tools/term-lookup.d.ts +15 -0
- package/dist/tools/term-lookup.js +16 -0
- package/dist/types/index.d.ts +32 -18
- package/dist/types/interchange.d.ts +66 -4
- package/dist/ui/fontawesome.d.ts +51 -0
- package/dist/ui/fontawesome.js +122 -0
- package/package.json +15 -6
- package/dist/formative/index.d.ts +0 -16
- package/dist/formative/index.js +0 -5
- package/dist/formative/session.d.ts +0 -19
- package/dist/i18n/messages/nl-NL.d.ts +0 -639
- package/dist/i18n/messages/nl-NL.js +0 -639
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ItemSessionControlPolicy, ResolvedItemSessionControlPolicy } from "./types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Built-in defaults. `enabled: false` is what makes this contract invisible to
|
|
4
|
-
* every section authored before it: an absent `
|
|
4
|
+
* every section authored before it: an absent `itemSessionControl` field resolves to a
|
|
5
5
|
* disabled policy, and a disabled policy produces no state, no projection and
|
|
6
6
|
* no control.
|
|
7
7
|
*
|
|
8
8
|
* `maxTries: 1` matches QTI 3's `qti-item-session-control@max-attempts` default.
|
|
9
9
|
*/
|
|
10
|
-
export declare const
|
|
10
|
+
export declare const ITEM_SESSION_CONTROL_DEFAULTS: ResolvedItemSessionControlPolicy;
|
|
11
11
|
/**
|
|
12
12
|
* Merge built-in defaults, the section policy, and one item ref's override, in
|
|
13
13
|
* that order. Each field overrides independently, which is the order QTI 3 uses
|
|
@@ -17,21 +17,21 @@ export declare const FORMATIVE_POLICY_DEFAULTS: ResolvedFormativePolicy;
|
|
|
17
17
|
* section: authored policy is host-supplied data, and a typo in one field should
|
|
18
18
|
* not cost the learner the whole item.
|
|
19
19
|
*/
|
|
20
|
-
export declare function
|
|
20
|
+
export declare function resolveItemSessionControl(sectionPolicy?: ItemSessionControlPolicy | null, itemPolicy?: ItemSessionControlPolicy | null): ResolvedItemSessionControlPolicy;
|
|
21
21
|
/**
|
|
22
22
|
* Resolve one policy per item in section order.
|
|
23
23
|
*
|
|
24
24
|
* Keyed by the identifier the caller supplies, which in the section runtime is
|
|
25
25
|
* the canonical item id — the same key `itemSessions` and the completion map
|
|
26
|
-
* use, so
|
|
26
|
+
* use, so Try state and an item session are always addressable by one
|
|
27
27
|
* id.
|
|
28
28
|
*/
|
|
29
|
-
export declare function
|
|
30
|
-
sectionPolicy?:
|
|
29
|
+
export declare function resolveItemSessionControls(args: {
|
|
30
|
+
sectionPolicy?: ItemSessionControlPolicy | null;
|
|
31
31
|
items: ReadonlyArray<{
|
|
32
32
|
identifier: string;
|
|
33
|
-
policy?:
|
|
33
|
+
policy?: ItemSessionControlPolicy | null;
|
|
34
34
|
}>;
|
|
35
|
-
}): Record<string,
|
|
36
|
-
/** True when at least one item in the section
|
|
37
|
-
export declare function
|
|
35
|
+
}): Record<string, ResolvedItemSessionControlPolicy>;
|
|
36
|
+
/** True when at least one item in the section enables item session control. */
|
|
37
|
+
export declare function isItemSessionControlEnabledInSection(policies: Record<string, ResolvedItemSessionControlPolicy>): boolean;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Built-in defaults. `enabled: false` is what makes this contract invisible to
|
|
3
|
-
* every section authored before it: an absent `
|
|
3
|
+
* every section authored before it: an absent `itemSessionControl` field resolves to a
|
|
4
4
|
* disabled policy, and a disabled policy produces no state, no projection and
|
|
5
5
|
* no control.
|
|
6
6
|
*
|
|
7
7
|
* `maxTries: 1` matches QTI 3's `qti-item-session-control@max-attempts` default.
|
|
8
8
|
*/
|
|
9
|
-
export const
|
|
9
|
+
export const ITEM_SESSION_CONTROL_DEFAULTS = {
|
|
10
10
|
enabled: false,
|
|
11
11
|
maxTries: 1,
|
|
12
12
|
feedback: "correctness",
|
|
@@ -40,21 +40,21 @@ function normalizeRevealTiming(value) {
|
|
|
40
40
|
* section: authored policy is host-supplied data, and a typo in one field should
|
|
41
41
|
* not cost the learner the whole item.
|
|
42
42
|
*/
|
|
43
|
-
export function
|
|
43
|
+
export function resolveItemSessionControl(sectionPolicy, itemPolicy) {
|
|
44
44
|
const enabled = typeof itemPolicy?.enabled === "boolean"
|
|
45
45
|
? itemPolicy.enabled
|
|
46
46
|
: typeof sectionPolicy?.enabled === "boolean"
|
|
47
47
|
? sectionPolicy.enabled
|
|
48
|
-
:
|
|
48
|
+
: ITEM_SESSION_CONTROL_DEFAULTS.enabled;
|
|
49
49
|
const maxTries = normalizeTryLimit(itemPolicy?.maxTries) ??
|
|
50
50
|
normalizeTryLimit(sectionPolicy?.maxTries) ??
|
|
51
|
-
|
|
51
|
+
ITEM_SESSION_CONTROL_DEFAULTS.maxTries;
|
|
52
52
|
const feedback = normalizeFeedback(itemPolicy?.feedback) ??
|
|
53
53
|
normalizeFeedback(sectionPolicy?.feedback) ??
|
|
54
|
-
|
|
54
|
+
ITEM_SESSION_CONTROL_DEFAULTS.feedback;
|
|
55
55
|
const requestedRevealOn = normalizeRevealTiming(itemPolicy?.revealOn) ??
|
|
56
56
|
normalizeRevealTiming(sectionPolicy?.revealOn) ??
|
|
57
|
-
|
|
57
|
+
ITEM_SESSION_CONTROL_DEFAULTS.revealOn;
|
|
58
58
|
// `"on-final-try"` has no referent under unlimited Tries — there is no final
|
|
59
59
|
// Try to reveal on — so it resolves to the only reading that keeps feedback
|
|
60
60
|
// reachable.
|
|
@@ -68,19 +68,19 @@ export function resolveFormativePolicy(sectionPolicy, itemPolicy) {
|
|
|
68
68
|
*
|
|
69
69
|
* Keyed by the identifier the caller supplies, which in the section runtime is
|
|
70
70
|
* the canonical item id — the same key `itemSessions` and the completion map
|
|
71
|
-
* use, so
|
|
71
|
+
* use, so Try state and an item session are always addressable by one
|
|
72
72
|
* id.
|
|
73
73
|
*/
|
|
74
|
-
export function
|
|
74
|
+
export function resolveItemSessionControls(args) {
|
|
75
75
|
const resolved = {};
|
|
76
76
|
for (const item of args.items) {
|
|
77
77
|
if (!item?.identifier)
|
|
78
78
|
continue;
|
|
79
|
-
resolved[item.identifier] =
|
|
79
|
+
resolved[item.identifier] = resolveItemSessionControl(args.sectionPolicy, item.policy);
|
|
80
80
|
}
|
|
81
81
|
return resolved;
|
|
82
82
|
}
|
|
83
|
-
/** True when at least one item in the section
|
|
84
|
-
export function
|
|
83
|
+
/** True when at least one item in the section enables item session control. */
|
|
84
|
+
export function isItemSessionControlEnabledInSection(policies) {
|
|
85
85
|
return Object.values(policies).some((policy) => policy.enabled);
|
|
86
86
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ItemTryState, TrySectionSlice } from "./types.js";
|
|
2
|
+
export declare const TRY_SLICE_VERSION: 1;
|
|
3
|
+
export declare function toTrySectionSlice(states: Record<string, ItemTryState>): TrySectionSlice;
|
|
4
|
+
/**
|
|
5
|
+
* Validate a persisted slice against the section it is being restored into.
|
|
6
|
+
*
|
|
7
|
+
* Returns `null` for a slice this build cannot read, which the caller treats as
|
|
8
|
+
* "start Try state clean". Rejection is deliberately narrow: it discards
|
|
9
|
+
* Try progress and nothing else, so a version bump here never costs a
|
|
10
|
+
* learner their responses — item sessions travel in the same snapshot and are
|
|
11
|
+
* normalized separately.
|
|
12
|
+
*
|
|
13
|
+
* An absent slice is `null` too, and indistinguishable from a save made
|
|
14
|
+
* without Try state, which is what keeps existing snapshots valid.
|
|
15
|
+
*/
|
|
16
|
+
export declare function normalizeTrySectionSlice(args: {
|
|
17
|
+
slice: unknown;
|
|
18
|
+
allowedItemIdentifiers: readonly string[];
|
|
19
|
+
}): Record<string, ItemTryState> | null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export const
|
|
1
|
+
import { ITEM_TRY_STATE_VERSION } from "./state.js";
|
|
2
|
+
export const TRY_SLICE_VERSION = 1;
|
|
3
3
|
const CORRECTNESS_VALUES = [
|
|
4
4
|
"correct",
|
|
5
5
|
"partial",
|
|
@@ -54,14 +54,14 @@ function normalizeItemState(itemIdentifier, value) {
|
|
|
54
54
|
if (!value || typeof value !== "object")
|
|
55
55
|
return null;
|
|
56
56
|
const raw = value;
|
|
57
|
-
if (raw.version !==
|
|
57
|
+
if (raw.version !== ITEM_TRY_STATE_VERSION)
|
|
58
58
|
return null;
|
|
59
59
|
const tryCount = readNonNegativeInteger(raw.tryCount);
|
|
60
60
|
if (tryCount === undefined)
|
|
61
61
|
return null;
|
|
62
62
|
const firstCorrectTry = readNonNegativeInteger(raw.firstCorrectTry);
|
|
63
63
|
return {
|
|
64
|
-
version:
|
|
64
|
+
version: ITEM_TRY_STATE_VERSION,
|
|
65
65
|
itemIdentifier,
|
|
66
66
|
tryCount,
|
|
67
67
|
revealed: raw.revealed === true,
|
|
@@ -72,9 +72,9 @@ function normalizeItemState(itemIdentifier, value) {
|
|
|
72
72
|
revealOverride: normalizeRevealOverride(raw.revealOverride),
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
|
-
export function
|
|
75
|
+
export function toTrySectionSlice(states) {
|
|
76
76
|
return {
|
|
77
|
-
version:
|
|
77
|
+
version: TRY_SLICE_VERSION,
|
|
78
78
|
items: { ...states },
|
|
79
79
|
};
|
|
80
80
|
}
|
|
@@ -82,20 +82,20 @@ export function toFormativeSectionSlice(states) {
|
|
|
82
82
|
* Validate a persisted slice against the section it is being restored into.
|
|
83
83
|
*
|
|
84
84
|
* Returns `null` for a slice this build cannot read, which the caller treats as
|
|
85
|
-
* "start
|
|
86
|
-
*
|
|
85
|
+
* "start Try state clean". Rejection is deliberately narrow: it discards
|
|
86
|
+
* Try progress and nothing else, so a version bump here never costs a
|
|
87
87
|
* learner their responses — item sessions travel in the same snapshot and are
|
|
88
88
|
* normalized separately.
|
|
89
89
|
*
|
|
90
|
-
* An absent slice is `null` too, and indistinguishable from a
|
|
91
|
-
*
|
|
90
|
+
* An absent slice is `null` too, and indistinguishable from a save made
|
|
91
|
+
* without Try state, which is what keeps existing snapshots valid.
|
|
92
92
|
*/
|
|
93
|
-
export function
|
|
93
|
+
export function normalizeTrySectionSlice(args) {
|
|
94
94
|
const { slice } = args;
|
|
95
95
|
if (!slice || typeof slice !== "object")
|
|
96
96
|
return null;
|
|
97
97
|
const raw = slice;
|
|
98
|
-
if (raw.version !==
|
|
98
|
+
if (raw.version !== TRY_SLICE_VERSION)
|
|
99
99
|
return null;
|
|
100
100
|
if (!raw.items || typeof raw.items !== "object")
|
|
101
101
|
return null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
export declare const
|
|
3
|
-
export declare function
|
|
1
|
+
import type { FeedbackReveal, ItemTryState, ItemTryView, TryOutcome, ResolvedItemSessionControlPolicy } from "./types.js";
|
|
2
|
+
export declare const ITEM_TRY_STATE_VERSION: 1;
|
|
3
|
+
export declare function createItemTryState(itemIdentifier: string): ItemTryState;
|
|
4
4
|
/**
|
|
5
5
|
* Record one Try.
|
|
6
6
|
*
|
|
@@ -8,24 +8,24 @@ export declare function createFormativeItemState(itemIdentifier: string): Format
|
|
|
8
8
|
* state can be checked, so a second click landing before the projection
|
|
9
9
|
* round-trips is dropped rather than spending a Try.
|
|
10
10
|
*/
|
|
11
|
-
export declare function
|
|
12
|
-
state?:
|
|
11
|
+
export declare function recordTry(args: {
|
|
12
|
+
state?: ItemTryState;
|
|
13
13
|
itemIdentifier: string;
|
|
14
|
-
policy:
|
|
15
|
-
outcome:
|
|
16
|
-
}):
|
|
14
|
+
policy: ResolvedItemSessionControlPolicy;
|
|
15
|
+
outcome: TryOutcome;
|
|
16
|
+
}): ItemTryState;
|
|
17
17
|
/**
|
|
18
18
|
* Dismiss a reveal and reopen the item for editing. Withdrawing `revealed` is
|
|
19
19
|
* what withdraws the env override, so the item returns to the section's own
|
|
20
20
|
* mode.
|
|
21
21
|
*
|
|
22
22
|
* A learner action, so it respects the Try budget: an item with none left keeps
|
|
23
|
-
* its feedback on screen. `
|
|
23
|
+
* its feedback on screen. `hideItemFeedback` is the host-authority version.
|
|
24
24
|
*/
|
|
25
|
-
export declare function
|
|
26
|
-
state?:
|
|
27
|
-
policy:
|
|
28
|
-
}):
|
|
25
|
+
export declare function retryItem(args: {
|
|
26
|
+
state?: ItemTryState;
|
|
27
|
+
policy: ResolvedItemSessionControlPolicy;
|
|
28
|
+
}): ItemTryState | undefined;
|
|
29
29
|
/**
|
|
30
30
|
* Reveal on host authority — a teacher-driven "show the answer".
|
|
31
31
|
*
|
|
@@ -38,23 +38,23 @@ export declare function retryFormativeItem(args: {
|
|
|
38
38
|
* whatever response is there, including none, which is what "show the answer"
|
|
39
39
|
* means before the learner has answered.
|
|
40
40
|
*/
|
|
41
|
-
export declare function
|
|
42
|
-
state?:
|
|
41
|
+
export declare function revealItemFeedback(args: {
|
|
42
|
+
state?: ItemTryState;
|
|
43
43
|
itemIdentifier: string;
|
|
44
|
-
policy:
|
|
45
|
-
feedback: Exclude<
|
|
46
|
-
}):
|
|
44
|
+
policy: ResolvedItemSessionControlPolicy;
|
|
45
|
+
feedback: Exclude<FeedbackReveal, "none">;
|
|
46
|
+
}): ItemTryState;
|
|
47
47
|
/**
|
|
48
48
|
* Withdraw a reveal on host authority. Unlike a learner retry this ignores the
|
|
49
49
|
* Try budget: a host that revealed an item with no Tries left must be able to
|
|
50
50
|
* put it back.
|
|
51
51
|
*/
|
|
52
|
-
export declare function
|
|
53
|
-
state?:
|
|
54
|
-
policy:
|
|
55
|
-
}):
|
|
52
|
+
export declare function hideItemFeedback(args: {
|
|
53
|
+
state?: ItemTryState;
|
|
54
|
+
policy: ResolvedItemSessionControlPolicy;
|
|
55
|
+
}): ItemTryState | undefined;
|
|
56
56
|
/** Derive everything a rendering component needs from policy plus state. */
|
|
57
|
-
export declare function
|
|
58
|
-
policy:
|
|
59
|
-
state?:
|
|
60
|
-
}):
|
|
57
|
+
export declare function resolveItemTryView(args: {
|
|
58
|
+
policy: ResolvedItemSessionControlPolicy;
|
|
59
|
+
state?: ItemTryState;
|
|
60
|
+
}): ItemTryView;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export const
|
|
2
|
-
export function
|
|
1
|
+
export const ITEM_TRY_STATE_VERSION = 1;
|
|
2
|
+
export function createItemTryState(itemIdentifier) {
|
|
3
3
|
return {
|
|
4
|
-
version:
|
|
4
|
+
version: ITEM_TRY_STATE_VERSION,
|
|
5
5
|
itemIdentifier,
|
|
6
6
|
tryCount: 0,
|
|
7
7
|
revealed: false,
|
|
@@ -20,7 +20,7 @@ function shouldReveal(policy, tryCount) {
|
|
|
20
20
|
return false;
|
|
21
21
|
if (policy.revealOn === "on-try")
|
|
22
22
|
return true;
|
|
23
|
-
// Defensive: `
|
|
23
|
+
// Defensive: `resolveItemSessionControl` already coerces `"on-final-try"` to
|
|
24
24
|
// `"on-try"` under unlimited Tries, so this branch only guards a
|
|
25
25
|
// hand-constructed policy.
|
|
26
26
|
if (policy.maxTries === "unlimited")
|
|
@@ -34,8 +34,8 @@ function shouldReveal(policy, tryCount) {
|
|
|
34
34
|
* state can be checked, so a second click landing before the projection
|
|
35
35
|
* round-trips is dropped rather than spending a Try.
|
|
36
36
|
*/
|
|
37
|
-
export function
|
|
38
|
-
const current = args.state ??
|
|
37
|
+
export function recordTry(args) {
|
|
38
|
+
const current = args.state ?? createItemTryState(args.itemIdentifier);
|
|
39
39
|
if (!args.policy.enabled)
|
|
40
40
|
return current;
|
|
41
41
|
const remaining = triesRemaining(args.policy, current.tryCount);
|
|
@@ -44,7 +44,7 @@ export function recordFormativeTry(args) {
|
|
|
44
44
|
const tryCount = current.tryCount + 1;
|
|
45
45
|
return {
|
|
46
46
|
...current,
|
|
47
|
-
version:
|
|
47
|
+
version: ITEM_TRY_STATE_VERSION,
|
|
48
48
|
itemIdentifier: args.itemIdentifier,
|
|
49
49
|
tryCount,
|
|
50
50
|
revealed: shouldReveal(args.policy, tryCount),
|
|
@@ -62,9 +62,9 @@ export function recordFormativeTry(args) {
|
|
|
62
62
|
* mode.
|
|
63
63
|
*
|
|
64
64
|
* A learner action, so it respects the Try budget: an item with none left keeps
|
|
65
|
-
* its feedback on screen. `
|
|
65
|
+
* its feedback on screen. `hideItemFeedback` is the host-authority version.
|
|
66
66
|
*/
|
|
67
|
-
export function
|
|
67
|
+
export function retryItem(args) {
|
|
68
68
|
const current = args.state;
|
|
69
69
|
if (!current || !args.policy.enabled)
|
|
70
70
|
return current;
|
|
@@ -87,8 +87,8 @@ export function retryFormativeItem(args) {
|
|
|
87
87
|
* whatever response is there, including none, which is what "show the answer"
|
|
88
88
|
* means before the learner has answered.
|
|
89
89
|
*/
|
|
90
|
-
export function
|
|
91
|
-
const current = args.state ??
|
|
90
|
+
export function revealItemFeedback(args) {
|
|
91
|
+
const current = args.state ?? createItemTryState(args.itemIdentifier);
|
|
92
92
|
if (!args.policy.enabled)
|
|
93
93
|
return current;
|
|
94
94
|
if (current.revealed && current.revealOverride === args.feedback) {
|
|
@@ -101,7 +101,7 @@ export function revealFormativeItem(args) {
|
|
|
101
101
|
* Try budget: a host that revealed an item with no Tries left must be able to
|
|
102
102
|
* put it back.
|
|
103
103
|
*/
|
|
104
|
-
export function
|
|
104
|
+
export function hideItemFeedback(args) {
|
|
105
105
|
const current = args.state;
|
|
106
106
|
if (!current || !args.policy.enabled)
|
|
107
107
|
return current;
|
|
@@ -119,7 +119,7 @@ function envOverrideFor(policy, revealed, revealOverride) {
|
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
121
|
/** Derive everything a rendering component needs from policy plus state. */
|
|
122
|
-
export function
|
|
122
|
+
export function resolveItemTryView(args) {
|
|
123
123
|
const { policy } = args;
|
|
124
124
|
const tryCount = args.state?.tryCount ?? 0;
|
|
125
125
|
const revealed = args.state?.revealed === true;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Item session control vocabulary.
|
|
3
3
|
*
|
|
4
4
|
* A **Try** is one submitted-for-checking pass over a single item. It is not
|
|
5
5
|
* `TestAttemptSession` (the assessment administration) and not
|
|
@@ -7,11 +7,11 @@
|
|
|
7
7
|
* an item). Both of those already own the word "attempt" in this codebase, at
|
|
8
8
|
* two different scopes, which is why this contract does not use it.
|
|
9
9
|
*
|
|
10
|
-
* See `docs/prds/
|
|
10
|
+
* See `docs/prds/item-session-control-contract.md` for the ratified contract and
|
|
11
11
|
* its QTI 3 mapping.
|
|
12
12
|
*/
|
|
13
13
|
/** How many Tries a learner gets on one item. */
|
|
14
|
-
export type
|
|
14
|
+
export type TryLimit = number | "unlimited";
|
|
15
15
|
/**
|
|
16
16
|
* What the learner sees after a Try.
|
|
17
17
|
*
|
|
@@ -20,54 +20,52 @@ export type FormativeTryLimit = number | "unlimited";
|
|
|
20
20
|
* `role: "instructor"`, which is the element convention for additionally
|
|
21
21
|
* revealing the authored correct response.
|
|
22
22
|
*/
|
|
23
|
-
export type
|
|
23
|
+
export type FeedbackReveal = "none" | "correctness" | "solution";
|
|
24
24
|
/** When a reveal happens: after every Try, or only once Tries are spent. */
|
|
25
|
-
export type
|
|
25
|
+
export type FeedbackRevealTiming = "on-try" | "on-final-try";
|
|
26
26
|
/**
|
|
27
|
-
* Authored policy. Lives on `AssessmentSection.
|
|
28
|
-
* default and on `AssessmentItemRef.
|
|
27
|
+
* Authored policy. Lives on `AssessmentSection.itemSessionControl` as the section
|
|
28
|
+
* default and on `AssessmentItemRef.itemSessionControl` as a per-item override.
|
|
29
29
|
*/
|
|
30
|
-
export interface
|
|
30
|
+
export interface ItemSessionControlPolicy {
|
|
31
31
|
/**
|
|
32
32
|
* Absent or `false` leaves delivery exactly as it is without this contract:
|
|
33
33
|
* no control, no projection, no state.
|
|
34
34
|
*/
|
|
35
35
|
enabled?: boolean;
|
|
36
36
|
/** Default 1. */
|
|
37
|
-
maxTries?:
|
|
37
|
+
maxTries?: TryLimit;
|
|
38
38
|
/** Default `"correctness"`. */
|
|
39
|
-
feedback?:
|
|
39
|
+
feedback?: FeedbackReveal;
|
|
40
40
|
/** Default `"on-try"`. */
|
|
41
|
-
revealOn?:
|
|
41
|
+
revealOn?: FeedbackRevealTiming;
|
|
42
42
|
}
|
|
43
|
-
/** Same shape as the section policy; every field overrides independently. */
|
|
44
|
-
export type FormativeItemPolicy = FormativeDeliveryPolicy;
|
|
45
43
|
/** The policy after built-in defaults, section, and item ref are merged. */
|
|
46
|
-
export interface
|
|
44
|
+
export interface ResolvedItemSessionControlPolicy {
|
|
47
45
|
enabled: boolean;
|
|
48
|
-
maxTries:
|
|
49
|
-
feedback:
|
|
50
|
-
revealOn:
|
|
46
|
+
maxTries: TryLimit;
|
|
47
|
+
feedback: FeedbackReveal;
|
|
48
|
+
revealOn: FeedbackRevealTiming;
|
|
51
49
|
}
|
|
52
50
|
/**
|
|
53
51
|
* `"unknown"` is the honest state for an item no loaded controller can score —
|
|
54
52
|
* one holding a rubric element, or one whose element bundle exposes no
|
|
55
53
|
* `outcome`. It is never collapsed into `"incorrect"`.
|
|
56
54
|
*/
|
|
57
|
-
export type
|
|
55
|
+
export type TryCorrectness = "correct" | "partial" | "incorrect" | "unknown";
|
|
58
56
|
/**
|
|
59
57
|
* The subset of an element outcome that aggregation reads. `OutcomeResponse` is
|
|
60
58
|
* assignable to this; the structural type keeps this module free of a
|
|
61
59
|
* dependency on the entity types, which import policy types from here.
|
|
62
60
|
*/
|
|
63
|
-
export interface
|
|
61
|
+
export interface ScoredElementOutcome {
|
|
64
62
|
score?: number;
|
|
65
63
|
max?: number;
|
|
66
64
|
[key: string]: unknown;
|
|
67
65
|
}
|
|
68
66
|
/** The recorded result of one Try. */
|
|
69
|
-
export interface
|
|
70
|
-
correctness:
|
|
67
|
+
export interface TryOutcome {
|
|
68
|
+
correctness: TryCorrectness;
|
|
71
69
|
points?: number;
|
|
72
70
|
max?: number;
|
|
73
71
|
scoredElementCount: number;
|
|
@@ -84,10 +82,10 @@ export interface FormativeTryOutcome {
|
|
|
84
82
|
* A trade: this persists inside the session slice, so a snapshot grows by
|
|
85
83
|
* whatever the elements put in their outcomes — some include a scoring trace.
|
|
86
84
|
*/
|
|
87
|
-
elementOutcomes?:
|
|
85
|
+
elementOutcomes?: ScoredElementOutcome[];
|
|
88
86
|
}
|
|
89
|
-
/** Per-item delivery state. Persisted; see `
|
|
90
|
-
export interface
|
|
87
|
+
/** Per-item delivery state. Persisted; see `TrySectionSlice`. */
|
|
88
|
+
export interface ItemTryState {
|
|
91
89
|
version: 1;
|
|
92
90
|
itemIdentifier: string;
|
|
93
91
|
tryCount: number;
|
|
@@ -98,7 +96,7 @@ export interface FormativeItemState {
|
|
|
98
96
|
* reload restores the screen the learner left.
|
|
99
97
|
*/
|
|
100
98
|
revealed: boolean;
|
|
101
|
-
lastOutcome?:
|
|
99
|
+
lastOutcome?: TryOutcome;
|
|
102
100
|
/** 1-based Try on which the item first scored full credit. Never overwritten. */
|
|
103
101
|
firstCorrectTry?: number;
|
|
104
102
|
/**
|
|
@@ -106,19 +104,19 @@ export interface FormativeItemState {
|
|
|
106
104
|
* than the learner earning it. Overrides the policy's `feedback` for the env
|
|
107
105
|
* projection only — the policy still decides Tries.
|
|
108
106
|
*
|
|
109
|
-
* Set by `
|
|
107
|
+
* Set by `revealItemFeedback`, cleared by `hideItemFeedback` and by a
|
|
110
108
|
* learner retry, so a teacher-driven "show the answer" does not silently
|
|
111
109
|
* upgrade every later reveal on that item.
|
|
112
110
|
*/
|
|
113
|
-
revealOverride?:
|
|
111
|
+
revealOverride?: FeedbackReveal;
|
|
114
112
|
}
|
|
115
|
-
/** The persisted slice, carried on `SectionControllerSessionState.
|
|
116
|
-
export interface
|
|
113
|
+
/** The persisted slice, carried on `SectionControllerSessionState.tries`. */
|
|
114
|
+
export interface TrySectionSlice {
|
|
117
115
|
version: 1;
|
|
118
|
-
items: Record<string,
|
|
116
|
+
items: Record<string, ItemTryState>;
|
|
119
117
|
}
|
|
120
118
|
/** Projected over the section env for one revealed item. */
|
|
121
|
-
export interface
|
|
119
|
+
export interface FeedbackEnvOverride {
|
|
122
120
|
mode: "evaluate";
|
|
123
121
|
role: "student" | "instructor";
|
|
124
122
|
}
|
|
@@ -126,18 +124,18 @@ export interface FormativeEnvOverride {
|
|
|
126
124
|
* What a rendering component needs, derived once so no component reimplements
|
|
127
125
|
* the predicate. Carries no copy — wording belongs to the rendering package.
|
|
128
126
|
*/
|
|
129
|
-
export interface
|
|
127
|
+
export interface ItemTryView {
|
|
130
128
|
enabled: boolean;
|
|
131
129
|
tryCount: number;
|
|
132
130
|
triesRemaining: number | "unlimited";
|
|
133
131
|
canCheck: boolean;
|
|
134
132
|
canRetry: boolean;
|
|
135
133
|
revealed: boolean;
|
|
136
|
-
envOverride?:
|
|
137
|
-
lastOutcome?:
|
|
134
|
+
envOverride?: FeedbackEnvOverride;
|
|
135
|
+
lastOutcome?: TryOutcome;
|
|
138
136
|
}
|
|
139
137
|
/** Section rollup over Try outcomes. */
|
|
140
|
-
export interface
|
|
138
|
+
export interface MasteryRollup {
|
|
141
139
|
version: 1;
|
|
142
140
|
totalItems: number;
|
|
143
141
|
/**
|
|
@@ -153,12 +151,12 @@ export interface FormativeMasteryRollup {
|
|
|
153
151
|
complete: boolean;
|
|
154
152
|
}
|
|
155
153
|
/** The composition-model projection layouts read. */
|
|
156
|
-
export interface
|
|
154
|
+
export interface TrySectionProjection {
|
|
157
155
|
version: 1;
|
|
158
156
|
enabled: boolean;
|
|
159
|
-
policies: Record<string,
|
|
160
|
-
states: Record<string,
|
|
161
|
-
mastery:
|
|
157
|
+
policies: Record<string, ResolvedItemSessionControlPolicy>;
|
|
158
|
+
states: Record<string, ItemTryState>;
|
|
159
|
+
mastery: MasteryRollup;
|
|
162
160
|
}
|
|
163
161
|
/** The two learner actions a rendering component can request. */
|
|
164
|
-
export type
|
|
162
|
+
export type TryAction = "check" | "retry";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Item session control vocabulary.
|
|
3
3
|
*
|
|
4
4
|
* A **Try** is one submitted-for-checking pass over a single item. It is not
|
|
5
5
|
* `TestAttemptSession` (the assessment administration) and not
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* an item). Both of those already own the word "attempt" in this codebase, at
|
|
8
8
|
* two different scopes, which is why this contract does not use it.
|
|
9
9
|
*
|
|
10
|
-
* See `docs/prds/
|
|
10
|
+
* See `docs/prds/item-session-control-contract.md` for the ratified contract and
|
|
11
11
|
* its QTI 3 mapping.
|
|
12
12
|
*/
|
|
13
13
|
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hostname classification for a URL a server fetches on someone else's behalf,
|
|
3
|
+
* such as an SSML `<audio src>` a TTS provider will request or an asset URL an
|
|
4
|
+
* upstream service returns. Every such check reads this module, so no two
|
|
5
|
+
* copies of it can drift apart.
|
|
6
|
+
*
|
|
7
|
+
* IP literals are classified by `ipaddr.js`, whose ranges follow the IANA IPv4
|
|
8
|
+
* and IPv6 Special-Purpose Address Registries (RFC 6890). A host is private
|
|
9
|
+
* unless it is a public unicast address or a name: loopback, private, link-local,
|
|
10
|
+
* unique-local, carrier-grade NAT, multicast, broadcast, documentation and
|
|
11
|
+
* reserved ranges, and the IPv6 forms that embed an IPv4 address (NAT64, 6to4,
|
|
12
|
+
* Teredo), all count. `ipaddr.js` also reads the numeric IPv4 forms (`2130706433`,
|
|
13
|
+
* `0x7f000001`, `0177.0.0.1`) and unmaps IPv4-mapped IPv6.
|
|
14
|
+
*
|
|
15
|
+
* Only the literal is checked. A public name that resolves to a private address
|
|
16
|
+
* passes; guarding that takes a check of the resolved address at connect time.
|
|
17
|
+
*
|
|
18
|
+
* @module
|
|
19
|
+
*/
|
|
20
|
+
/** Whether `hostname` is a cloud instance-metadata endpoint. */
|
|
21
|
+
export declare function isCloudMetadataHostname(hostname: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Whether `hostname` is anything other than a public unicast address or a
|
|
24
|
+
* public name: `localhost` and its subdomains (RFC 6761), or an IP literal in
|
|
25
|
+
* any special-purpose range.
|
|
26
|
+
*/
|
|
27
|
+
export declare function isPrivateHostname(hostname: string): boolean;
|