@step-wise/input-exercises 0.1.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.
Files changed (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +245 -0
  3. package/dist/InputExercise/checkInput.d.ts +8 -0
  4. package/dist/InputExercise/checkInput.d.ts.map +1 -0
  5. package/dist/InputExercise/checkInput.js +26 -0
  6. package/dist/InputExercise/getInput.d.ts +24 -0
  7. package/dist/InputExercise/getInput.d.ts.map +1 -0
  8. package/dist/InputExercise/getInput.js +23 -0
  9. package/dist/InputExercise/guards.d.ts +4 -0
  10. package/dist/InputExercise/guards.d.ts.map +1 -0
  11. package/dist/InputExercise/guards.js +30 -0
  12. package/dist/InputExercise/history.d.ts +22 -0
  13. package/dist/InputExercise/history.d.ts.map +1 -0
  14. package/dist/InputExercise/history.js +135 -0
  15. package/dist/InputExercise/index.d.ts +6 -0
  16. package/dist/InputExercise/index.d.ts.map +1 -0
  17. package/dist/InputExercise/index.js +5 -0
  18. package/dist/InputExercise/parameterSerialization.d.ts +5 -0
  19. package/dist/InputExercise/parameterSerialization.d.ts.map +1 -0
  20. package/dist/InputExercise/parameterSerialization.js +15 -0
  21. package/dist/InputExercise/reducerSupport.d.ts +41 -0
  22. package/dist/InputExercise/reducerSupport.d.ts.map +1 -0
  23. package/dist/InputExercise/reducerSupport.js +82 -0
  24. package/dist/InputExercise/solutions.d.ts +7 -0
  25. package/dist/InputExercise/solutions.d.ts.map +1 -0
  26. package/dist/InputExercise/solutions.js +15 -0
  27. package/dist/InputExercise/types.d.ts +70 -0
  28. package/dist/InputExercise/types.d.ts.map +1 -0
  29. package/dist/InputExercise/types.js +1 -0
  30. package/dist/InputExercise/valueOperations.d.ts +4 -0
  31. package/dist/InputExercise/valueOperations.d.ts.map +1 -0
  32. package/dist/InputExercise/valueOperations.js +14 -0
  33. package/dist/MonoExercise/guards.d.ts +3 -0
  34. package/dist/MonoExercise/guards.d.ts.map +1 -0
  35. package/dist/MonoExercise/guards.js +4 -0
  36. package/dist/MonoExercise/index.d.ts +4 -0
  37. package/dist/MonoExercise/index.d.ts.map +1 -0
  38. package/dist/MonoExercise/index.js +3 -0
  39. package/dist/MonoExercise/reducer.d.ts +4 -0
  40. package/dist/MonoExercise/reducer.d.ts.map +1 -0
  41. package/dist/MonoExercise/reducer.js +87 -0
  42. package/dist/MonoExercise/types.d.ts +16 -0
  43. package/dist/MonoExercise/types.d.ts.map +1 -0
  44. package/dist/MonoExercise/types.js +1 -0
  45. package/dist/StepExercise/guards.d.ts +3 -0
  46. package/dist/StepExercise/guards.d.ts.map +1 -0
  47. package/dist/StepExercise/guards.js +4 -0
  48. package/dist/StepExercise/history.d.ts +10 -0
  49. package/dist/StepExercise/history.d.ts.map +1 -0
  50. package/dist/StepExercise/history.js +57 -0
  51. package/dist/StepExercise/index.d.ts +6 -0
  52. package/dist/StepExercise/index.d.ts.map +1 -0
  53. package/dist/StepExercise/index.js +5 -0
  54. package/dist/StepExercise/preprocessing.d.ts +8 -0
  55. package/dist/StepExercise/preprocessing.d.ts.map +1 -0
  56. package/dist/StepExercise/preprocessing.js +27 -0
  57. package/dist/StepExercise/reducer.d.ts +4 -0
  58. package/dist/StepExercise/reducer.d.ts.map +1 -0
  59. package/dist/StepExercise/reducer.js +217 -0
  60. package/dist/StepExercise/types.d.ts +38 -0
  61. package/dist/StepExercise/types.d.ts.map +1 -0
  62. package/dist/StepExercise/types.js +1 -0
  63. package/dist/guards.d.ts +5 -0
  64. package/dist/guards.d.ts.map +1 -0
  65. package/dist/guards.js +5 -0
  66. package/dist/index.d.ts +5 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +4 -0
  69. package/dist/tsconfig.tsbuildinfo +1 -0
  70. package/package.json +53 -0
@@ -0,0 +1,41 @@
1
+ import { type ExerciseAction, type ExerciseMode, type ExerciseState, type UpdateSkills } from '@step-wise/exercise-definition';
2
+ import type { SerializedData } from '@step-wise/serialization';
3
+ import type { InputDependency, InputExerciseParameters, InputExerciseReport, InputExerciseValueOperations } from './types.ts';
4
+ export type SoloInputExerciseDependencyState = Partial<{
5
+ inputDependency: SerializedData;
6
+ }>;
7
+ export type GroupInputExerciseDependencyState = Partial<{
8
+ inputDependencies: Record<string, SerializedData>;
9
+ }>;
10
+ export type InputExerciseDependencyState = SoloInputExerciseDependencyState & GroupInputExerciseDependencyState;
11
+ export type SoloInputExerciseAttemptState = Partial<{
12
+ attempted: true;
13
+ }>;
14
+ export type GroupInputExerciseAttemptState = Partial<{
15
+ attemptedBy: string[];
16
+ }>;
17
+ export type InputExerciseAttemptState = SoloInputExerciseAttemptState & GroupInputExerciseAttemptState;
18
+ type InputExerciseUserAction<TAction extends ExerciseAction> = {
19
+ userId?: string;
20
+ action: TAction;
21
+ };
22
+ export type InputExerciseReducerInput<TAction extends ExerciseAction, TState extends ExerciseState, TParameters extends InputExerciseParameters> = {
23
+ mode: ExerciseMode;
24
+ actions: readonly InputExerciseUserAction<TAction>[];
25
+ parameters: TParameters;
26
+ state: TState;
27
+ updateSkills?: UpdateSkills;
28
+ };
29
+ export type InputExerciseActionsReduction<TState extends ExerciseState> = {
30
+ state: TState;
31
+ reports: (InputExerciseReport | undefined)[];
32
+ };
33
+ export declare function hasAttempted(state: InputExerciseAttemptState, mode: ExerciseMode, userId?: string): boolean;
34
+ export declare function addAttemptsToState<TState extends InputExerciseAttemptState>(state: TState, mode: ExerciseMode, userIds: readonly (string | undefined)[]): TState;
35
+ export declare function getInputDependency<TInputDependency = InputDependency>(state: InputExerciseDependencyState, mode: ExerciseMode, valueOperations: InputExerciseValueOperations, userId?: string): TInputDependency | undefined;
36
+ export declare function setInputDependencies<TState extends InputExerciseDependencyState>(state: TState, mode: ExerciseMode, dependencies: readonly {
37
+ userId?: string;
38
+ value: unknown;
39
+ }[], valueOperations: InputExerciseValueOperations): TState;
40
+ export {};
41
+ //# sourceMappingURL=reducerSupport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducerSupport.d.ts","sourceRoot":"","sources":["../../src/InputExercise/reducerSupport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,KAAK,YAAY,EAAgC,MAAM,gCAAgC,CAAA;AAC5J,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAA;AAG7H,MAAM,MAAM,gCAAgC,GAAG,OAAO,CAAC;IAAE,eAAe,EAAE,cAAc,CAAA;CAAE,CAAC,CAAA;AAC3F,MAAM,MAAM,iCAAiC,GAAG,OAAO,CAAC;IAAE,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;CAAE,CAAC,CAAA;AAC9G,MAAM,MAAM,4BAA4B,GAAG,gCAAgC,GAAG,iCAAiC,CAAA;AAG/G,MAAM,MAAM,6BAA6B,GAAG,OAAO,CAAC;IAAE,SAAS,EAAE,IAAI,CAAA;CAAE,CAAC,CAAA;AACxE,MAAM,MAAM,8BAA8B,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAAA;AAC/E,MAAM,MAAM,yBAAyB,GAAG,6BAA6B,GAAG,8BAA8B,CAAA;AAGtG,KAAK,uBAAuB,CAAC,OAAO,SAAS,cAAc,IAAI;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;CACf,CAAA;AACD,MAAM,MAAM,yBAAyB,CAAC,OAAO,SAAS,cAAc,EAAE,MAAM,SAAS,aAAa,EAAE,WAAW,SAAS,uBAAuB,IAAI;IAClJ,IAAI,EAAE,YAAY,CAAA;IAClB,OAAO,EAAE,SAAS,uBAAuB,CAAC,OAAO,CAAC,EAAE,CAAA;IACpD,UAAU,EAAE,WAAW,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,YAAY,CAAA;CAC3B,CAAA;AAGD,MAAM,MAAM,6BAA6B,CAAC,MAAM,SAAS,aAAa,IAAI;IACzE,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,CAAC,mBAAmB,GAAG,SAAS,CAAC,EAAE,CAAA;CAC5C,CAAA;AAGD,wBAAgB,YAAY,CAAC,KAAK,EAAE,yBAAyB,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAY3G;AAGD,wBAAgB,kBAAkB,CAAC,MAAM,SAAS,yBAAyB,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,GAAG,MAAM,CAkBhK;AAGD,wBAAgB,kBAAkB,CAAC,gBAAgB,GAAG,eAAe,EAAE,KAAK,EAAE,4BAA4B,EAAE,IAAI,EAAE,YAAY,EAAE,eAAe,EAAE,4BAA4B,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAe5N;AAGD,wBAAgB,oBAAoB,CAAC,MAAM,SAAS,4BAA4B,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,EAAE,EAAE,eAAe,EAAE,4BAA4B,GAAG,MAAM,CAyBxO"}
@@ -0,0 +1,82 @@
1
+ import { throwUnsupportedExerciseMode } from '@step-wise/exercise-definition';
2
+ // Check in the state if a user has attempted an exercise (or step).
3
+ export function hasAttempted(state, mode, userId) {
4
+ switch (mode) {
5
+ case 'solo':
6
+ return state.attempted === true;
7
+ case 'group':
8
+ if (userId === undefined)
9
+ throw new TypeError(`A userId is required when checking attempts for a group exercise.`);
10
+ return state.attemptedBy?.includes(userId) ?? false;
11
+ default:
12
+ return throwUnsupportedExerciseMode(mode);
13
+ }
14
+ }
15
+ // Update the state to note that the given users attempted the exercise (or step).
16
+ export function addAttemptsToState(state, mode, userIds) {
17
+ if (userIds.length === 0)
18
+ return state;
19
+ switch (mode) {
20
+ case 'solo':
21
+ return { ...state, attempted: true };
22
+ case 'group': {
23
+ const attemptedBy = new Set(state.attemptedBy);
24
+ userIds.forEach(userId => {
25
+ if (userId === undefined)
26
+ throw new TypeError(`A userId is required when registering an attempt for a group exercise.`);
27
+ attemptedBy.add(userId);
28
+ });
29
+ return { ...state, attemptedBy: [...attemptedBy] };
30
+ }
31
+ default:
32
+ return throwUnsupportedExerciseMode(mode);
33
+ }
34
+ }
35
+ // Get the input dependency for the given user (or solo) from the state, deserializing it for use.
36
+ export function getInputDependency(state, mode, valueOperations, userId) {
37
+ switch (mode) {
38
+ case 'solo':
39
+ return state.inputDependency === undefined ? undefined : valueOperations.deserialize(state.inputDependency);
40
+ case 'group': {
41
+ if (userId === undefined)
42
+ throw new TypeError(`A userId is required when getting an input dependency for a group exercise.`);
43
+ const userDependency = state.inputDependencies?.[userId];
44
+ if (userDependency !== undefined)
45
+ return valueOperations.deserialize(userDependency);
46
+ return undefined;
47
+ }
48
+ default:
49
+ return throwUnsupportedExerciseMode(mode);
50
+ }
51
+ }
52
+ // Apply the given input dependencies to the state, serializing them for storage.
53
+ export function setInputDependencies(state, mode, dependencies, valueOperations) {
54
+ if (dependencies.length === 0)
55
+ return state;
56
+ switch (mode) {
57
+ case 'solo': {
58
+ const { inputDependency: _, ...stateWithoutDependency } = state;
59
+ if (dependencies.length > 1)
60
+ throw new TypeError(`Only one input dependency can be set for a solo exercise.`);
61
+ const dependency = dependencies[0].value;
62
+ return (dependency === undefined ? stateWithoutDependency : { ...stateWithoutDependency, inputDependency: valueOperations.serialize(dependency) });
63
+ }
64
+ case 'group': {
65
+ const inputDependencies = { ...state.inputDependencies };
66
+ dependencies.forEach(({ userId, value }) => {
67
+ if (userId === undefined)
68
+ throw new TypeError(`A userId is required when setting an input dependency for a group exercise.`);
69
+ if (value === undefined)
70
+ delete inputDependencies[userId];
71
+ else
72
+ inputDependencies[userId] = valueOperations.serialize(value);
73
+ });
74
+ if (Object.keys(inputDependencies).length > 0)
75
+ return { ...state, inputDependencies };
76
+ const { inputDependencies: _, ...stateWithoutInputDependencies } = state;
77
+ return stateWithoutInputDependencies;
78
+ }
79
+ default:
80
+ return throwUnsupportedExerciseMode(mode);
81
+ }
82
+ }
@@ -0,0 +1,7 @@
1
+ import type { InputDependency, InputExerciseMetadata, InputExerciseParameters, InputExerciseSolution, InputExerciseSpec, UpdateInputDependencyData } from './types.ts';
2
+ type SolutionCallbacks<TParameters extends InputExerciseParameters, TSolution extends InputExerciseSolution, TInputDependency> = Pick<InputExerciseSpec<InputExerciseMetadata, TParameters, TSolution, TInputDependency>, 'getStaticSolution' | 'updateInputDependency' | 'getSolution'>;
3
+ export declare function resolveStaticSolution<TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution, TInputDependency = InputDependency>(definition: SolutionCallbacks<TParameters, TSolution, TInputDependency>, parameters: TParameters): Promise<Partial<TSolution>>;
4
+ export declare function resolveUpdatedInputDependency<TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution, TInputDependency = InputDependency>(definition: SolutionCallbacks<TParameters, TSolution, TInputDependency>, data: UpdateInputDependencyData<TParameters, TSolution, TInputDependency>): Promise<TInputDependency | undefined>;
5
+ export declare function resolveSolution<TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution, TInputDependency = InputDependency>(definition: SolutionCallbacks<TParameters, TSolution, TInputDependency>, parameters: TParameters, inputDependency: TInputDependency | undefined, staticSolution: Partial<TSolution>): Promise<TSolution | undefined>;
6
+ export {};
7
+ //# sourceMappingURL=solutions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"solutions.d.ts","sourceRoot":"","sources":["../../src/InputExercise/solutions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAA;AAEtK,KAAK,iBAAiB,CACrB,WAAW,SAAS,uBAAuB,EAC3C,SAAS,SAAS,qBAAqB,EAAE,gBAAgB,IAAI,IAAI,CAAC,iBAAiB,CAAC,qBAAqB,EAAE,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,EAAE,mBAAmB,GAAG,uBAAuB,GAAG,aAAa,CAClN,CAAA;AAGF,wBAAsB,qBAAqB,CAC1C,WAAW,SAAS,uBAAuB,GAAG,uBAAuB,EACrE,SAAS,SAAS,qBAAqB,GAAG,qBAAqB,EAC/D,gBAAgB,GAAG,eAAe,EAElC,UAAU,EAAE,iBAAiB,CAAC,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,EACvE,UAAU,EAAE,WAAW,GACrB,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAE7B;AAGD,wBAAsB,6BAA6B,CAClD,WAAW,SAAS,uBAAuB,GAAG,uBAAuB,EACrE,SAAS,SAAS,qBAAqB,GAAG,qBAAqB,EAC/D,gBAAgB,GAAG,eAAe,EAElC,UAAU,EAAE,iBAAiB,CAAC,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,EACvE,IAAI,EAAE,yBAAyB,CAAC,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,GACvE,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAEvC;AAGD,wBAAsB,eAAe,CACpC,WAAW,SAAS,uBAAuB,GAAG,uBAAuB,EACrE,SAAS,SAAS,qBAAqB,GAAG,qBAAqB,EAC/D,gBAAgB,GAAG,eAAe,EAElC,UAAU,EAAE,iBAAiB,CAAC,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,EACvE,UAAU,EAAE,WAAW,EACvB,eAAe,EAAE,gBAAgB,GAAG,SAAS,EAC7C,cAAc,EAAE,OAAO,CAAC,SAAS,CAAC,GAChC,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAIhC"}
@@ -0,0 +1,15 @@
1
+ // Resolve the reusable input-independent portion of a solution.
2
+ export async function resolveStaticSolution(definition, parameters) {
3
+ return definition.getStaticSolution === undefined ? {} : await definition.getStaticSolution(parameters);
4
+ }
5
+ // Update the dependency with the input submitted for the current exercise step.
6
+ export async function resolveUpdatedInputDependency(definition, data) {
7
+ return definition.updateInputDependency === undefined ? data.previousInputDependency : await definition.updateInputDependency(data);
8
+ }
9
+ // Resolve the complete solution by combining its static and dynamic portions.
10
+ export async function resolveSolution(definition, parameters, inputDependency, staticSolution) {
11
+ if (definition.getSolution === undefined)
12
+ return undefined;
13
+ const dynamicSolution = await definition.getSolution(parameters, inputDependency, staticSolution);
14
+ return { ...staticSolution, ...dynamicSolution };
15
+ }
@@ -0,0 +1,70 @@
1
+ import type { Awaitable, PlainDataObject } from '@step-wise/js-utils';
2
+ import type { Exercise, ExerciseMetadata, ExerciseState, GroupExerciseReducer, SoloExerciseReducer } from '@step-wise/exercise-definition';
3
+ import type { SerializedData } from '@step-wise/serialization';
4
+ import type { InputValue } from '@step-wise/input-interpretation';
5
+ import type { ValueTypes } from '@step-wise/value-types';
6
+ export type InputExerciseMetadata = ExerciseMetadata & {
7
+ comparisons?: Record<string, unknown>;
8
+ };
9
+ export type InputExerciseRawInput = Record<string, InputValue>;
10
+ export type InputExerciseAction = {
11
+ type: 'input';
12
+ input: InputExerciseRawInput;
13
+ adoptUserHistory?: string;
14
+ } | {
15
+ type: 'giveUp';
16
+ };
17
+ export type InputExerciseActionType = InputExerciseAction['type'];
18
+ export type InputExerciseParameters = Record<string, unknown>;
19
+ export type InputExerciseInput = Record<string, unknown>;
20
+ export type InputExerciseReport = PlainDataObject;
21
+ export type SoloInputExerciseReport = InputExerciseReport;
22
+ export type GroupInputExerciseReport = Record<string, InputExerciseReport>;
23
+ export type CheckInputResult = boolean | {
24
+ correct: boolean;
25
+ report?: InputExerciseReport;
26
+ };
27
+ export type InputDependency = unknown;
28
+ export type UpdateInputDependencyData<TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution, TInputDependency = InputDependency> = {
29
+ parameters: TParameters;
30
+ previousInputDependency: TInputDependency | undefined;
31
+ staticSolution: Partial<TSolution>;
32
+ input: InputExerciseInput;
33
+ step: number;
34
+ };
35
+ export type UpdateInputDependency<TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution, TInputDependency = InputDependency> = (data: UpdateInputDependencyData<TParameters, TSolution, TInputDependency>) => Awaitable<TInputDependency | undefined>;
36
+ export type InputExerciseSolution = Record<string, unknown>;
37
+ export type GetStaticSolution<TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution> = (parameters: TParameters) => Awaitable<Partial<TSolution>>;
38
+ export type GetSolution<TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution, TInputDependency = InputDependency> = (parameters: TParameters, inputDependency: TInputDependency | undefined, staticSolution: Partial<TSolution>) => Awaitable<Partial<TSolution>>;
39
+ export type InputExerciseSpec<TMetadata extends InputExerciseMetadata, TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution, TInputDependency = InputDependency> = {
40
+ metadata: TMetadata;
41
+ valueTypes?: ValueTypes;
42
+ generateParameters?: (example: boolean) => Awaitable<TParameters>;
43
+ updateInputDependency?: UpdateInputDependency<TParameters, TSolution, TInputDependency>;
44
+ getStaticSolution?: GetStaticSolution<TParameters, TSolution>;
45
+ getSolution?: GetSolution<TParameters, TSolution, TInputDependency>;
46
+ };
47
+ export type InputExerciseValueOperations = {
48
+ serialize: (value: unknown) => SerializedData;
49
+ deserialize: (value: unknown) => unknown;
50
+ interpretInput: (input: InputExerciseRawInput) => InputExerciseInput;
51
+ toInputValue: (value: unknown, type: string) => InputValue;
52
+ areValuesEqual: (type: string, inputValue: unknown, expectedValue: unknown, options?: unknown) => boolean;
53
+ };
54
+ export type InputExercise<TMetadata extends InputExerciseMetadata, TAction extends InputExerciseAction, TState extends ExerciseState, TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution, TInputDependency = InputDependency> = Exercise<TMetadata, TAction, TState, PlainDataObject, SoloInputExerciseReport, GroupInputExerciseReport> & Omit<InputExerciseSpec<TMetadata, TParameters, TSolution, TInputDependency>, 'generateParameters' | 'valueTypes'> & {
55
+ valueOperations: InputExerciseValueOperations;
56
+ generateParameters: (example: boolean) => Promise<PlainDataObject>;
57
+ getInitialState: (parameters: PlainDataObject) => Awaitable<TState>;
58
+ processSoloAction: SoloExerciseReducer<TAction, TState, PlainDataObject, SoloInputExerciseReport>;
59
+ processGroupActions: GroupExerciseReducer<TAction, TState, PlainDataObject, GroupInputExerciseReport>;
60
+ };
61
+ export type CheckInputData<TMetadata extends InputExerciseMetadata = InputExerciseMetadata, TParameters extends InputExerciseParameters = InputExerciseParameters, TInputDependency = InputDependency, TSolution extends InputExerciseSolution = InputExerciseSolution> = {
62
+ metadata: TMetadata;
63
+ parameters: TParameters;
64
+ rawInput: InputExerciseRawInput;
65
+ input: InputExerciseInput;
66
+ inputDependency: TInputDependency | undefined;
67
+ solution?: TSolution;
68
+ areValuesEqual: InputExerciseValueOperations['areValuesEqual'];
69
+ };
70
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/InputExercise/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ import { type ValueTypes } from '@step-wise/value-types';
2
+ import type { InputExerciseValueOperations } from './types.ts';
3
+ export declare function createInputExerciseValueOperations(valueTypes?: ValueTypes): InputExerciseValueOperations;
4
+ //# sourceMappingURL=valueOperations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valueOperations.d.ts","sourceRoot":"","sources":["../../src/InputExercise/valueOperations.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,UAAU,EAAsE,MAAM,wBAAwB,CAAA;AAE5H,OAAO,KAAK,EAAsB,4BAA4B,EAAE,MAAM,YAAY,CAAA;AAElF,wBAAgB,kCAAkC,CAAC,UAAU,GAAE,UAAe,GAAG,4BAA4B,CAS5G"}
@@ -0,0 +1,14 @@
1
+ import { deserializeData, serializeData } from '@step-wise/serialization';
2
+ import { interpretInputData, toInputValue } from '@step-wise/input-interpretation';
3
+ import { createAreValuesEqual } from '@step-wise/value-equality';
4
+ import { combineValueTypes, extractValueTypeAdapters, fundamentalValueTypes } from '@step-wise/value-types';
5
+ export function createInputExerciseValueOperations(valueTypes = {}) {
6
+ const adapters = extractValueTypeAdapters(combineValueTypes(fundamentalValueTypes, valueTypes));
7
+ return {
8
+ serialize: value => serializeData(value, adapters.serializationAdapters),
9
+ deserialize: value => deserializeData(value, adapters.serializationAdapters),
10
+ interpretInput: input => interpretInputData(input, adapters.inputValueAdapters),
11
+ toInputValue: (value, type) => toInputValue(value, type, adapters.inputValueAdapters),
12
+ areValuesEqual: createAreValuesEqual(adapters.equalityAdapters),
13
+ };
14
+ }
@@ -0,0 +1,3 @@
1
+ import type { MonoExercise } from './types.ts';
2
+ export declare function isMonoExercise(value: unknown): value is MonoExercise<any, any>;
3
+ //# sourceMappingURL=guards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../src/MonoExercise/guards.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAE9E"}
@@ -0,0 +1,4 @@
1
+ import { hasInputExerciseProperties } from '../InputExercise/guards.js';
2
+ export function isMonoExercise(value) {
3
+ return hasInputExerciseProperties(value) && value.type === 'mono';
4
+ }
@@ -0,0 +1,4 @@
1
+ export * from './types.ts';
2
+ export * from './guards.ts';
3
+ export * from './reducer.ts';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/MonoExercise/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA"}
@@ -0,0 +1,3 @@
1
+ export * from './types.js';
2
+ export * from './guards.js';
3
+ export * from './reducer.js';
@@ -0,0 +1,4 @@
1
+ import { type InputDependency, type InputExerciseParameters, type InputExerciseSolution } from '../InputExercise/index.ts';
2
+ import type { MonoExercise, MonoExerciseSpec } from './types.ts';
3
+ export declare function buildMonoExercise<TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution, TInputDependency = InputDependency>(spec: MonoExerciseSpec<TParameters, TSolution, TInputDependency>): MonoExercise<TParameters, TSolution, TInputDependency>;
4
+ //# sourceMappingURL=reducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/MonoExercise/reducer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiC,KAAK,eAAe,EAA4B,KAAK,uBAAuB,EAAE,KAAK,qBAAqB,EAA0I,MAAM,2BAA2B,CAAA;AAM3T,OAAO,KAAK,EAAqB,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAGnF,wBAAgB,iBAAiB,CAAC,WAAW,SAAS,uBAAuB,GAAG,uBAAuB,EAAE,SAAS,SAAS,qBAAqB,GAAG,qBAAqB,EAAE,gBAAgB,GAAG,eAAe,EAAE,IAAI,EAAE,gBAAgB,CAAC,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,GAAG,YAAY,CAAC,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAYtU"}
@@ -0,0 +1,87 @@
1
+ import { resolveExerciseParameters } from '@step-wise/exercise-definition';
2
+ import { resolveSolution, resolveStaticSolution, resolveUpdatedInputDependency } from '../InputExercise/index.js';
3
+ import { getGroupInputExerciseReport, normalizeCheckInputResult } from '../InputExercise/checkInput.js';
4
+ import { deserializeInputExerciseParameters, serializeInputExerciseParameters } from '../InputExercise/parameterSerialization.js';
5
+ import { createInputExerciseValueOperations } from '../InputExercise/valueOperations.js';
6
+ import { addAttemptsToState, getInputDependency, hasAttempted, setInputDependencies } from '../InputExercise/reducerSupport.js';
7
+ // Build a MonoExercise from its author-facing spec.
8
+ export function buildMonoExercise(spec) {
9
+ const { valueTypes, ...definition } = spec;
10
+ const valueOperations = createInputExerciseValueOperations(valueTypes);
11
+ return {
12
+ ...definition,
13
+ valueOperations,
14
+ type: 'mono',
15
+ generateParameters: async (example) => serializeInputExerciseParameters(await resolveExerciseParameters(spec.generateParameters, example), valueOperations.serialize),
16
+ getInitialState: () => ({}),
17
+ processSoloAction: buildMonoExerciseSoloReducer(spec, valueOperations),
18
+ processGroupActions: buildMonoExerciseGroupReducer(spec, valueOperations),
19
+ };
20
+ }
21
+ // Build the solo reducer for a MonoExercise, which processes actions for a single user.
22
+ function buildMonoExerciseSoloReducer(spec, valueOperations) {
23
+ return async (reducerInput) => {
24
+ const runtimeInput = { ...reducerInput, parameters: deserializeInputExerciseParameters(reducerInput.parameters, valueOperations.deserialize) };
25
+ if ('done' in runtimeInput.state && runtimeInput.state.done)
26
+ return { state: runtimeInput.state };
27
+ const { state, reports } = await reduceActions(spec, { ...runtimeInput, mode: 'solo', actions: [{ action: reducerInput.action }] }, valueOperations);
28
+ return reports[0] === undefined ? { state } : { state, report: reports[0] };
29
+ };
30
+ }
31
+ // Build the group reducer for a MonoExercise, which processes actions from multiple users.
32
+ function buildMonoExerciseGroupReducer(spec, valueOperations) {
33
+ return async (reducerInput) => {
34
+ if (reducerInput.actions.length === 0)
35
+ throw new Error(`Cannot resolve a group exercise without actions.`);
36
+ const runtimeInput = { ...reducerInput, parameters: deserializeInputExerciseParameters(reducerInput.parameters, valueOperations.deserialize), mode: 'group' };
37
+ if ('done' in runtimeInput.state && runtimeInput.state.done)
38
+ return { state: runtimeInput.state };
39
+ const { state, reports } = await reduceActions(spec, runtimeInput, valueOperations);
40
+ const report = getGroupInputExerciseReport(reducerInput.actions.map(({ userId }) => userId), reports);
41
+ return report === undefined ? { state } : { state, report };
42
+ };
43
+ }
44
+ // Reduce a normalized set of solo or group actions.
45
+ async function reduceActions(spec, reducerInput, valueOperations) {
46
+ const { metadata, checkInput } = spec;
47
+ const { mode, state, actions, parameters, updateSkills } = reducerInput;
48
+ let newState = addAttemptsToState(state, mode, actions.filter(userAction => userAction.action.type === 'input').map(userAction => userAction.userId));
49
+ // Run the checkInput function for all input actions, and also update the inputDependencies.
50
+ const staticSolution = actions.some(userAction => userAction.action.type === 'input') ? await resolveStaticSolution(spec, parameters) : {};
51
+ const results = await Promise.all(actions.map(async (userAction) => {
52
+ if (userAction.action.type !== 'input')
53
+ return { correct: false };
54
+ const input = valueOperations.interpretInput(userAction.action.input);
55
+ const previousInputDependency = getInputDependency(state, mode, valueOperations, mode === 'group' ? userAction.action.adoptUserHistory ?? userAction.userId : userAction.userId);
56
+ const inputDependency = await resolveUpdatedInputDependency(spec, { parameters, previousInputDependency, staticSolution, input: input, step: 0 });
57
+ const solution = await resolveSolution(spec, parameters, inputDependency, staticSolution);
58
+ const result = normalizeCheckInputResult(await checkInput({ metadata, parameters, rawInput: userAction.action.input, input, inputDependency, solution, areValuesEqual: valueOperations.areValuesEqual }));
59
+ return { ...result, inputDependency };
60
+ }));
61
+ if (spec.updateInputDependency !== undefined)
62
+ newState = setInputDependencies(newState, mode, results.flatMap((result, index) => 'inputDependency' in result ? [{ userId: actions[index].userId, value: result.inputDependency }] : []), valueOperations);
63
+ // Determine if the exercise is solved or given up, and update skills if applicable.
64
+ const correct = results.map(result => result.correct);
65
+ const someCorrect = correct.some(isCorrect => isCorrect);
66
+ const allGaveUp = actions.every(userAction => userAction.action.type === 'giveUp');
67
+ const isDone = someCorrect || allGaveUp;
68
+ if (updateSkills !== undefined) {
69
+ actions.forEach((userAction, index) => {
70
+ const { action, userId } = userAction;
71
+ if (action.type === 'input' || (isDone && !hasAttempted(state, mode, userId))) {
72
+ if (metadata.skill)
73
+ updateSkills(metadata.skill, correct[index], userId);
74
+ if (metadata.setup)
75
+ updateSkills(metadata.setup, correct[index], userId);
76
+ }
77
+ });
78
+ }
79
+ // Update the state and return it together with the reports.
80
+ const reports = results.map(result => 'report' in result ? result.report : undefined);
81
+ if (someCorrect)
82
+ return { state: { ...newState, solved: true, done: true }, reports };
83
+ else if (allGaveUp)
84
+ return { state: { ...newState, givenUp: true, done: true }, reports };
85
+ else
86
+ return { state: newState, reports };
87
+ }
@@ -0,0 +1,16 @@
1
+ import type { Awaitable } from '@step-wise/js-utils';
2
+ import type { InputExerciseMetadata, InputExerciseAction, InputExerciseAttemptState, InputExerciseDependencyState, InputExerciseParameters, CheckInputData, CheckInputResult, InputDependency, InputExercise, InputExerciseSpec, InputExerciseSolution } from '../InputExercise/index.ts';
3
+ export type MonoExerciseMetadata = InputExerciseMetadata;
4
+ export type MonoExerciseState = InputExerciseAttemptState & InputExerciseDependencyState & Partial<{
5
+ solved: true;
6
+ givenUp: true;
7
+ done: true;
8
+ }>;
9
+ export type MonoExerciseCheckInput<TParameters extends InputExerciseParameters = InputExerciseParameters, TInputDependency = InputDependency, TSolution extends InputExerciseSolution = InputExerciseSolution> = (data: CheckInputData<MonoExerciseMetadata, TParameters, TInputDependency, TSolution>) => Awaitable<CheckInputResult>;
10
+ export type MonoExerciseSpec<TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution, TInputDependency = InputDependency> = InputExerciseSpec<MonoExerciseMetadata, TParameters, TSolution, TInputDependency> & {
11
+ checkInput: MonoExerciseCheckInput<TParameters, TInputDependency, TSolution>;
12
+ };
13
+ export type MonoExercise<TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution, TInputDependency = InputDependency> = InputExercise<MonoExerciseMetadata, InputExerciseAction, MonoExerciseState, TParameters, TSolution, TInputDependency> & Omit<MonoExerciseSpec<TParameters, TSolution, TInputDependency>, 'generateParameters' | 'valueTypes'> & {
14
+ type: 'mono';
15
+ };
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/MonoExercise/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAEpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,4BAA4B,EAAE,uBAAuB,EAAE,cAAc,EAAE,gBAAgB,EAAE,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAA;AAEzR,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,CAAA;AAGxD,MAAM,MAAM,iBAAiB,GAAG,yBAAyB,GAAG,4BAA4B,GAAG,OAAO,CAAC;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,CAAC,CAAA;AAG/I,MAAM,MAAM,sBAAsB,CAAC,WAAW,SAAS,uBAAuB,GAAG,uBAAuB,EAAE,gBAAgB,GAAG,eAAe,EAAE,SAAS,SAAS,qBAAqB,GAAG,qBAAqB,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,oBAAoB,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,CAAC,KAAK,SAAS,CAAC,gBAAgB,CAAC,CAAA;AAGtU,MAAM,MAAM,gBAAgB,CAAC,WAAW,SAAS,uBAAuB,GAAG,uBAAuB,EAAE,SAAS,SAAS,qBAAqB,GAAG,qBAAqB,EAAE,gBAAgB,GAAG,eAAe,IAAI,iBAAiB,CAAC,oBAAoB,EAAE,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,GAAG;IAAE,UAAU,EAAE,sBAAsB,CAAC,WAAW,EAAE,gBAAgB,EAAE,SAAS,CAAC,CAAA;CAAE,CAAA;AAG/W,MAAM,MAAM,YAAY,CAAC,WAAW,SAAS,uBAAuB,GAAG,uBAAuB,EAAE,SAAS,SAAS,qBAAqB,GAAG,qBAAqB,EAAE,gBAAgB,GAAG,eAAe,IAAI,aAAa,CAAC,oBAAoB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,EAAE,oBAAoB,GAAG,YAAY,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { StepExercise } from './types.ts';
2
+ export declare function isStepExercise(value: unknown): value is StepExercise<any, any>;
3
+ //# sourceMappingURL=guards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guards.d.ts","sourceRoot":"","sources":["../../src/StepExercise/guards.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAE9E"}
@@ -0,0 +1,4 @@
1
+ import { hasInputExerciseProperties } from '../InputExercise/guards.js';
2
+ export function isStepExercise(value) {
3
+ return hasInputExerciseProperties(value) && value.type === 'step' && Array.isArray(value.metadata.steps);
4
+ }
@@ -0,0 +1,10 @@
1
+ import type { InputExerciseHistoryData, LastInputOptions } from '../InputExercise/history.ts';
2
+ import type { InputExerciseInput, InputExerciseRawInput, InputExerciseValueOperations } from '../InputExercise/types.ts';
3
+ import type { StepExerciseState } from './types.ts';
4
+ export declare function getCurrentStep(state: StepExerciseState | Record<string, never>): number;
5
+ export declare function getLastRawInputAtStep(instance: InputExerciseHistoryData<StepExerciseState>, step: number, userId?: string, options?: LastInputOptions): InputExerciseRawInput | undefined;
6
+ export declare function getLastInputAtStep(exercise: {
7
+ valueOperations: InputExerciseValueOperations;
8
+ }, instance: InputExerciseHistoryData<StepExerciseState>, step: number, userId?: string, options?: LastInputOptions): InputExerciseInput | undefined;
9
+ export declare function hasPreviousInputAtStep(instance: InputExerciseHistoryData<StepExerciseState>, step: number, userId?: string): boolean;
10
+ //# sourceMappingURL=history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/StepExercise/history.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAC7F,OAAO,KAAK,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,MAAM,2BAA2B,CAAA;AAExH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGnD,wBAAgB,cAAc,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,CAEvF;AAGD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,wBAAwB,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,qBAAqB,GAAG,SAAS,CA8B7L;AAGD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE;IAAE,eAAe,EAAE,4BAA4B,CAAA;CAAE,EAAE,QAAQ,EAAE,wBAAwB,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,kBAAkB,GAAG,SAAS,CAGpP;AAUD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,wBAAwB,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAEpI"}
@@ -0,0 +1,57 @@
1
+ import { ensureInteger } from '@step-wise/js-utils';
2
+ import { throwUnsupportedExerciseMode } from '@step-wise/exercise-definition';
3
+ // Get the step which this exercise's state is at.
4
+ export function getCurrentStep(state) {
5
+ return 'step' in state && typeof state.step === 'number' ? state.step : 0;
6
+ }
7
+ // Get the last given raw input from the user at the given step.
8
+ export function getLastRawInputAtStep(instance, step, userId, options = {}) {
9
+ step = ensureInteger(step, { nonNegative: true });
10
+ const { resolvedOnly = false } = options;
11
+ const { mode } = instance;
12
+ switch (mode) {
13
+ case 'solo':
14
+ for (let index = instance.history.length - 1; index >= 0; index--) {
15
+ const userAction = instance.history[index].action;
16
+ if (userAction.type !== 'input')
17
+ continue;
18
+ if (getCurrentStep(getStateBeforeEvent(instance, index)) === step)
19
+ return userAction.input;
20
+ }
21
+ return undefined;
22
+ case 'group': {
23
+ if (userId === undefined)
24
+ throw new TypeError(`A userId is required when retrieving input from a group exercise history.`);
25
+ let historyUserId = userId;
26
+ for (let index = instance.history.length - 1; index >= 0; index--) {
27
+ const event = instance.history[index];
28
+ const userAction = event.actions.find(userAction => userAction.userId === historyUserId)?.action;
29
+ if (!userAction || userAction.type !== 'input')
30
+ continue;
31
+ if ((!resolvedOnly || 'state' in event) && getCurrentStep(getStateBeforeEvent(instance, index)) === step)
32
+ return userAction.input;
33
+ historyUserId = userAction.adoptUserHistory ?? historyUserId;
34
+ }
35
+ return undefined;
36
+ }
37
+ default:
38
+ return throwUnsupportedExerciseMode(mode);
39
+ }
40
+ }
41
+ // Get the last given input from the user at the given step and interpret all its values.
42
+ export function getLastInputAtStep(exercise, instance, step, userId, options = {}) {
43
+ const rawInput = getLastRawInputAtStep(instance, step, userId, options);
44
+ return rawInput === undefined ? undefined : exercise.valueOperations.interpretInput(rawInput);
45
+ }
46
+ function getStateBeforeEvent(instance, eventIndex) {
47
+ if (eventIndex === 0)
48
+ return instance.initialState;
49
+ const previousEvent = instance.history[eventIndex - 1];
50
+ if (!('state' in previousEvent))
51
+ throw new Error(`Cannot determine the state before an event because the preceding event is unresolved.`);
52
+ return previousEvent.state;
53
+ }
54
+ // Check if a user has made a previous input at the given step.
55
+ export function hasPreviousInputAtStep(instance, step, userId) {
56
+ return getLastRawInputAtStep(instance, step, userId) !== undefined;
57
+ }
@@ -0,0 +1,6 @@
1
+ export * from './types.ts';
2
+ export * from './guards.ts';
3
+ export { createStepExerciseMetadata } from './preprocessing.ts';
4
+ export * from './history.ts';
5
+ export * from './reducer.ts';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/StepExercise/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,OAAO,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAA;AAC/D,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA"}
@@ -0,0 +1,5 @@
1
+ export * from './types.js';
2
+ export * from './guards.js';
3
+ export { createStepExerciseMetadata } from './preprocessing.js';
4
+ export * from './history.js';
5
+ export * from './reducer.js';
@@ -0,0 +1,8 @@
1
+ import { type SkillSetup } from '@step-wise/skill-setup';
2
+ import type { StepExerciseSteps } from './types.ts';
3
+ export declare function createStepExerciseMetadata(steps: StepExerciseSteps): {
4
+ steps: StepExerciseSteps;
5
+ setup?: SkillSetup;
6
+ };
7
+ export declare function ensureStepExerciseSteps(steps: StepExerciseSteps): StepExerciseSteps;
8
+ //# sourceMappingURL=preprocessing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preprocessing.d.ts","sourceRoot":"","sources":["../../src/StepExercise/preprocessing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAyC,MAAM,wBAAwB,CAAA;AAE/F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAGnD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,iBAAiB,GAAG;IAAE,KAAK,EAAE,iBAAiB,CAAC;IAAC,KAAK,CAAC,EAAE,UAAU,CAAA;CAAE,CAOrH;AAGD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,CAMnF"}
@@ -0,0 +1,27 @@
1
+ import { ensureSetup, and } from '@step-wise/skill-setup';
2
+ // Create the metadata for a step exercise, including the steps and the combined setup of all steps.
3
+ export function createStepExerciseMetadata(steps) {
4
+ ensureStepExerciseSteps(steps);
5
+ const setup = getSetupFromSteps(steps);
6
+ return {
7
+ steps,
8
+ ...(setup === undefined ? {} : { setup }),
9
+ };
10
+ }
11
+ // Ensure that the steps are valid: an array of steps, where each step is either a SkillSetupLike or an array of SkillSetupLike with at least two substeps.
12
+ export function ensureStepExerciseSteps(steps) {
13
+ if (!Array.isArray(steps))
14
+ throw new Error(`Invalid steps: expected an array, but received "${steps}".`);
15
+ steps.forEach((step, index) => {
16
+ if (Array.isArray(step) && step.length < 2)
17
+ throw new Error(`Invalid step ${index + 1}: a substep array must contain at least two substeps.`);
18
+ });
19
+ return steps;
20
+ }
21
+ // Get the combined setup of all steps, or undefined if no steps are defined.
22
+ function getSetupFromSteps(steps) {
23
+ const definedSteps = steps.flat().filter(step => step !== undefined);
24
+ if (definedSteps.length === 0)
25
+ return undefined;
26
+ return and(...definedSteps.map(ensureSetup));
27
+ }
@@ -0,0 +1,4 @@
1
+ import { type InputDependency, type InputExerciseParameters, type InputExerciseSolution } from '../InputExercise/index.ts';
2
+ import type { StepExercise, StepExerciseSpec } from './types.ts';
3
+ export declare function buildStepExercise<TParameters extends InputExerciseParameters = InputExerciseParameters, TSolution extends InputExerciseSolution = InputExerciseSolution, TInputDependency = InputDependency>(spec: StepExerciseSpec<TParameters, TSolution, TInputDependency>): StepExercise<TParameters, TSolution, TInputDependency>;
4
+ //# sourceMappingURL=reducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reducer.d.ts","sourceRoot":"","sources":["../../src/StepExercise/reducer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAiC,KAAK,eAAe,EAAqD,KAAK,uBAAuB,EAAwD,KAAK,qBAAqB,EAA0I,MAAM,2BAA2B,CAAA;AAM1Y,OAAO,KAAK,EAAoE,YAAY,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAKlI,wBAAgB,iBAAiB,CAChC,WAAW,SAAS,uBAAuB,GAAG,uBAAuB,EACrE,SAAS,SAAS,qBAAqB,GAAG,qBAAqB,EAC/D,gBAAgB,GAAG,eAAe,EAElC,IAAI,EAAE,gBAAgB,CAAC,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,GAC9D,YAAY,CAAC,WAAW,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAaxD"}