@mathscan/math-exercise-engine 1.0.9 → 1.0.11

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/dist/index.d.ts CHANGED
@@ -191,12 +191,6 @@ export declare function compile(dsl: string): ExerciseJSON;
191
191
  */
192
192
  export declare function compileWithAST(dsl: string): {
193
193
  ast: {
194
- validation: {
195
- type: "SimpleValidation";
196
- } | {
197
- expression: string;
198
- type: "ExpressionValidation";
199
- };
200
194
  type: "ExerciseDocument";
201
195
  lines: ({
202
196
  type: "LineBlock";
@@ -324,6 +318,12 @@ export declare function compileWithAST(dsl: string): {
324
318
  value: string;
325
319
  type: "TextBlock";
326
320
  } | undefined;
321
+ validation?: {
322
+ type: "SimpleValidation";
323
+ } | {
324
+ expression: string;
325
+ type: "ExpressionValidation";
326
+ } | undefined;
327
327
  tools?: ({
328
328
  type: "DragOptions";
329
329
  options: string[];
@@ -334,12 +334,6 @@ export declare function compileWithAST(dsl: string): {
334
334
  })[] | undefined;
335
335
  };
336
336
  json: {
337
- validation: {
338
- mode: "simple" | "expression";
339
- expression?: string | undefined;
340
- answers?: Record<string, string | number> | undefined;
341
- variables?: string[] | undefined;
342
- };
343
337
  version: "2.0";
344
338
  layout: any[];
345
339
  widgets: Record<string, {
@@ -361,6 +355,12 @@ export declare function compileWithAST(dsl: string): {
361
355
  includeOperators?: boolean | undefined;
362
356
  includeDecimal?: boolean | undefined;
363
357
  }>;
358
+ validation?: {
359
+ mode: "simple" | "expression";
360
+ expression?: string | undefined;
361
+ answers?: Record<string, string | number> | undefined;
362
+ variables?: string[] | undefined;
363
+ } | undefined;
364
364
  id?: string | undefined;
365
365
  meta?: {
366
366
  createdAt?: string | undefined;
@@ -1314,7 +1314,7 @@ export declare function createDragOptions(options: string[]): DragOptionsTool;
1314
1314
  /**
1315
1315
  * Create an exercise document
1316
1316
  */
1317
- export declare function createExerciseDocument(question: z.infer<typeof TextBlockSchema> | undefined, lines: ContentBlock[], validation: z.infer<typeof ValidationNodeSchema>, tools?: z.infer<typeof ToolNodeSchema>[]): ExerciseDocument;
1317
+ export declare function createExerciseDocument(question: z.infer<typeof TextBlockSchema> | undefined, lines: ContentBlock[], validation?: z.infer<typeof ValidationNodeSchema>, tools?: z.infer<typeof ToolNodeSchema>[]): ExerciseDocument;
1318
1318
 
1319
1319
  /**
1320
1320
  * Create an expression validation node
@@ -2415,7 +2415,7 @@ export declare const ExerciseDocumentSchema: z.ZodObject<{
2415
2415
  includeOperators?: boolean | undefined;
2416
2416
  includeDecimal?: boolean | undefined;
2417
2417
  }>]>, "many">>;
2418
- validation: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
2418
+ validation: z.ZodOptional<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
2419
2419
  type: z.ZodLiteral<"SimpleValidation">;
2420
2420
  }, "strip", z.ZodTypeAny, {
2421
2421
  type: "SimpleValidation";
@@ -2430,14 +2430,8 @@ export declare const ExerciseDocumentSchema: z.ZodObject<{
2430
2430
  }, {
2431
2431
  expression: string;
2432
2432
  type: "ExpressionValidation";
2433
- }>]>;
2433
+ }>]>>;
2434
2434
  }, "strip", z.ZodTypeAny, {
2435
- validation: {
2436
- type: "SimpleValidation";
2437
- } | {
2438
- expression: string;
2439
- type: "ExpressionValidation";
2440
- };
2441
2435
  type: "ExerciseDocument";
2442
2436
  lines: ({
2443
2437
  type: "LineBlock";
@@ -2565,6 +2559,12 @@ export declare const ExerciseDocumentSchema: z.ZodObject<{
2565
2559
  value: string;
2566
2560
  type: "TextBlock";
2567
2561
  } | undefined;
2562
+ validation?: {
2563
+ type: "SimpleValidation";
2564
+ } | {
2565
+ expression: string;
2566
+ type: "ExpressionValidation";
2567
+ } | undefined;
2568
2568
  tools?: ({
2569
2569
  type: "DragOptions";
2570
2570
  options: string[];
@@ -2574,12 +2574,6 @@ export declare const ExerciseDocumentSchema: z.ZodObject<{
2574
2574
  includeDecimal?: boolean | undefined;
2575
2575
  })[] | undefined;
2576
2576
  }, {
2577
- validation: {
2578
- type: "SimpleValidation";
2579
- } | {
2580
- expression: string;
2581
- type: "ExpressionValidation";
2582
- };
2583
2577
  type: "ExerciseDocument";
2584
2578
  lines: ({
2585
2579
  type: "LineBlock";
@@ -2707,6 +2701,12 @@ export declare const ExerciseDocumentSchema: z.ZodObject<{
2707
2701
  value: string;
2708
2702
  type: "TextBlock";
2709
2703
  } | undefined;
2704
+ validation?: {
2705
+ type: "SimpleValidation";
2706
+ } | {
2707
+ expression: string;
2708
+ type: "ExpressionValidation";
2709
+ } | undefined;
2710
2710
  tools?: ({
2711
2711
  type: "DragOptions";
2712
2712
  options: string[];
@@ -2778,7 +2778,7 @@ export declare const ExerciseJSONSchema: z.ZodObject<{
2778
2778
  includeOperators?: boolean | undefined;
2779
2779
  includeDecimal?: boolean | undefined;
2780
2780
  }>]>>;
2781
- validation: z.ZodObject<{
2781
+ validation: z.ZodOptional<z.ZodObject<{
2782
2782
  mode: z.ZodEnum<["simple", "expression"]>;
2783
2783
  answers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodString, z.ZodNumber]>>>;
2784
2784
  expression: z.ZodOptional<z.ZodString>;
@@ -2793,7 +2793,7 @@ export declare const ExerciseJSONSchema: z.ZodObject<{
2793
2793
  expression?: string | undefined;
2794
2794
  answers?: Record<string, string | number> | undefined;
2795
2795
  variables?: string[] | undefined;
2796
- }>;
2796
+ }>>;
2797
2797
  meta: z.ZodOptional<z.ZodObject<{
2798
2798
  createdAt: z.ZodOptional<z.ZodString>;
2799
2799
  author: z.ZodOptional<z.ZodString>;
@@ -2811,12 +2811,6 @@ export declare const ExerciseJSONSchema: z.ZodObject<{
2811
2811
  difficulty?: number | undefined;
2812
2812
  }>>;
2813
2813
  }, "strip", z.ZodTypeAny, {
2814
- validation: {
2815
- mode: "simple" | "expression";
2816
- expression?: string | undefined;
2817
- answers?: Record<string, string | number> | undefined;
2818
- variables?: string[] | undefined;
2819
- };
2820
2814
  version: "2.0";
2821
2815
  layout: any[];
2822
2816
  widgets: Record<string, {
@@ -2838,6 +2832,12 @@ export declare const ExerciseJSONSchema: z.ZodObject<{
2838
2832
  includeOperators?: boolean | undefined;
2839
2833
  includeDecimal?: boolean | undefined;
2840
2834
  }>;
2835
+ validation?: {
2836
+ mode: "simple" | "expression";
2837
+ expression?: string | undefined;
2838
+ answers?: Record<string, string | number> | undefined;
2839
+ variables?: string[] | undefined;
2840
+ } | undefined;
2841
2841
  id?: string | undefined;
2842
2842
  meta?: {
2843
2843
  createdAt?: string | undefined;
@@ -2846,12 +2846,6 @@ export declare const ExerciseJSONSchema: z.ZodObject<{
2846
2846
  difficulty?: number | undefined;
2847
2847
  } | undefined;
2848
2848
  }, {
2849
- validation: {
2850
- mode: "simple" | "expression";
2851
- expression?: string | undefined;
2852
- answers?: Record<string, string | number> | undefined;
2853
- variables?: string[] | undefined;
2854
- };
2855
2849
  version: "2.0";
2856
2850
  layout: any[];
2857
2851
  widgets: Record<string, {
@@ -2873,6 +2867,12 @@ export declare const ExerciseJSONSchema: z.ZodObject<{
2873
2867
  includeOperators?: boolean | undefined;
2874
2868
  includeDecimal?: boolean | undefined;
2875
2869
  }>;
2870
+ validation?: {
2871
+ mode: "simple" | "expression";
2872
+ expression?: string | undefined;
2873
+ answers?: Record<string, string | number> | undefined;
2874
+ variables?: string[] | undefined;
2875
+ } | undefined;
2876
2876
  id?: string | undefined;
2877
2877
  meta?: {
2878
2878
  createdAt?: string | undefined;
@@ -4944,6 +4944,10 @@ export declare function validate(exercise: ExerciseJSON, userInputs: Record<stri
4944
4944
  */
4945
4945
  export declare function validateBooleanExpression(expression: string): string | null;
4946
4946
 
4947
+ declare type ValidatedExerciseJSON = ExerciseJSON & {
4948
+ validation: ValidationJSON;
4949
+ };
4950
+
4947
4951
  /**
4948
4952
  * Validate an exercise JSON object at runtime
4949
4953
  *
@@ -4962,12 +4966,6 @@ export declare function validateBooleanExpression(expression: string): string |
4962
4966
  * ```
4963
4967
  */
4964
4968
  export declare function validateExerciseJSON(data: unknown): {
4965
- validation: {
4966
- mode: "simple" | "expression";
4967
- expression?: string | undefined;
4968
- answers?: Record<string, string | number> | undefined;
4969
- variables?: string[] | undefined;
4970
- };
4971
4969
  version: "2.0";
4972
4970
  layout: any[];
4973
4971
  widgets: Record<string, {
@@ -4989,6 +4987,12 @@ export declare function validateExerciseJSON(data: unknown): {
4989
4987
  includeOperators?: boolean | undefined;
4990
4988
  includeDecimal?: boolean | undefined;
4991
4989
  }>;
4990
+ validation?: {
4991
+ mode: "simple" | "expression";
4992
+ expression?: string | undefined;
4993
+ answers?: Record<string, string | number> | undefined;
4994
+ variables?: string[] | undefined;
4995
+ } | undefined;
4992
4996
  id?: string | undefined;
4993
4997
  meta?: {
4994
4998
  createdAt?: string | undefined;
@@ -5015,12 +5019,6 @@ export declare function validateExerciseJSON(data: unknown): {
5015
5019
  * ```
5016
5020
  */
5017
5021
  export declare function validateExerciseJSONSafe(data: unknown): z.SafeParseReturnType<{
5018
- validation: {
5019
- mode: "simple" | "expression";
5020
- expression?: string | undefined;
5021
- answers?: Record<string, string | number> | undefined;
5022
- variables?: string[] | undefined;
5023
- };
5024
5022
  version: "2.0";
5025
5023
  layout: any[];
5026
5024
  widgets: Record<string, {
@@ -5042,6 +5040,12 @@ export declare function validateExerciseJSONSafe(data: unknown): z.SafeParseRetu
5042
5040
  includeOperators?: boolean | undefined;
5043
5041
  includeDecimal?: boolean | undefined;
5044
5042
  }>;
5043
+ validation?: {
5044
+ mode: "simple" | "expression";
5045
+ expression?: string | undefined;
5046
+ answers?: Record<string, string | number> | undefined;
5047
+ variables?: string[] | undefined;
5048
+ } | undefined;
5045
5049
  id?: string | undefined;
5046
5050
  meta?: {
5047
5051
  createdAt?: string | undefined;
@@ -5050,12 +5054,6 @@ export declare function validateExerciseJSONSafe(data: unknown): z.SafeParseRetu
5050
5054
  difficulty?: number | undefined;
5051
5055
  } | undefined;
5052
5056
  }, {
5053
- validation: {
5054
- mode: "simple" | "expression";
5055
- expression?: string | undefined;
5056
- answers?: Record<string, string | number> | undefined;
5057
- variables?: string[] | undefined;
5058
- };
5059
5057
  version: "2.0";
5060
5058
  layout: any[];
5061
5059
  widgets: Record<string, {
@@ -5077,6 +5075,12 @@ export declare function validateExerciseJSONSafe(data: unknown): z.SafeParseRetu
5077
5075
  includeOperators?: boolean | undefined;
5078
5076
  includeDecimal?: boolean | undefined;
5079
5077
  }>;
5078
+ validation?: {
5079
+ mode: "simple" | "expression";
5080
+ expression?: string | undefined;
5081
+ answers?: Record<string, string | number> | undefined;
5082
+ variables?: string[] | undefined;
5083
+ } | undefined;
5080
5084
  id?: string | undefined;
5081
5085
  meta?: {
5082
5086
  createdAt?: string | undefined;
@@ -5089,11 +5093,11 @@ export declare function validateExerciseJSONSafe(data: unknown): z.SafeParseRetu
5089
5093
  /**
5090
5094
  * Validate using expression mode
5091
5095
  *
5092
- * @param exercise - The exercise JSON
5096
+ * @param exercise - The exercise JSON (with validation present)
5093
5097
  * @param userInputs - User's answers keyed by widget ID (variable names)
5094
5098
  * @returns Validation result
5095
5099
  */
5096
- export declare function validateExpression(exercise: ExerciseJSON, userInputs: Record<string, string | number>): ValidationResult;
5100
+ export declare function validateExpression(exercise: ValidatedExerciseJSON, userInputs: Record<string, string | number>): ValidationResult;
5097
5101
 
5098
5102
  /**
5099
5103
  * Check if user inputs are valid (proper format, etc.)
@@ -5108,11 +5112,11 @@ export declare function validateInputFormats(exercise: ExerciseJSON, userInputs:
5108
5112
  /**
5109
5113
  * Validate using simple mode (predetermined answers)
5110
5114
  *
5111
- * @param exercise - The exercise JSON
5115
+ * @param exercise - The exercise JSON (with validation present)
5112
5116
  * @param userInputs - User's answers keyed by widget ID
5113
5117
  * @returns Validation result
5114
5118
  */
5115
- export declare function validateSimple(exercise: ExerciseJSON, userInputs: Record<string, string | number>): ValidationResult;
5119
+ export declare function validateSimple(exercise: ValidatedExerciseJSON, userInputs: Record<string, string | number>): ValidationResult;
5116
5120
 
5117
5121
  /**
5118
5122
  * Validate a widget JSON object at runtime
package/dist/index.js CHANGED
@@ -439,13 +439,16 @@ class MathExerciseParser extends chevrotain.CstParser {
439
439
  this.OPTION(() => {
440
440
  this.SUBRULE(this.question);
441
441
  });
442
- this.MANY2(() => {
443
- this.SUBRULE(this.contentBlock);
442
+ this.MANY2({
443
+ GATE: this.BACKTRACK(this.contentBlock),
444
+ DEF: () => this.SUBRULE(this.contentBlock)
444
445
  });
445
446
  this.MANY3(() => {
446
447
  this.SUBRULE(this.tool);
447
448
  });
448
- this.SUBRULE(this.validation);
449
+ this.OPTION2(() => {
450
+ this.SUBRULE(this.validation);
451
+ });
449
452
  this.MANY4(() => {
450
453
  this.OR2([
451
454
  { ALT: () => this.CONSUME2(Newline) },
@@ -666,7 +669,7 @@ class MathExerciseParser extends chevrotain.CstParser {
666
669
  this.MANY2(() => {
667
670
  this.CONSUME2(Space);
668
671
  });
669
- this.AT_LEAST_ONE2(() => {
672
+ this.MANY3(() => {
670
673
  this.CONSUME(Newline);
671
674
  });
672
675
  });
@@ -1203,7 +1206,7 @@ const ExerciseDocumentSchema = zod.z.object({
1203
1206
  question: TextBlockSchema.optional(),
1204
1207
  lines: zod.z.array(ContentBlockSchema),
1205
1208
  tools: zod.z.array(ToolNodeSchema).optional(),
1206
- validation: ValidationNodeSchema
1209
+ validation: ValidationNodeSchema.optional()
1207
1210
  });
1208
1211
  function createExerciseDocument(question, lines, validation, tools) {
1209
1212
  return ExerciseDocumentSchema.parse({
@@ -1230,7 +1233,7 @@ class NaturalDSLVisitor extends BaseCstVisitor {
1230
1233
  const question = ctx.question ? this.visit(ctx.question) : void 0;
1231
1234
  const contentBlocks = ctx.contentBlock ? ctx.contentBlock.map((cb) => this.visit(cb)) : [];
1232
1235
  const tools = ctx.tool ? ctx.tool.map((t) => this.visit(t)) : [];
1233
- const validation = this.visit(ctx.validation);
1236
+ const validation = ctx.validation ? this.visit(ctx.validation) : void 0;
1234
1237
  return createExerciseDocument(question, contentBlocks, validation, tools);
1235
1238
  }
1236
1239
  // ═══════════════════════════════════════════════════════════════════════════
@@ -1695,7 +1698,7 @@ const ExerciseJSONSchema = zod.z.object({
1695
1698
  id: zod.z.string().optional(),
1696
1699
  layout: zod.z.array(LayoutItemSchema),
1697
1700
  widgets: zod.z.record(WidgetJSONSchema),
1698
- validation: ValidationJSONSchema,
1701
+ validation: ValidationJSONSchema.optional(),
1699
1702
  meta: ExerciseMetaSchema.optional()
1700
1703
  });
1701
1704
  function validateExerciseJSON(data) {
@@ -1852,13 +1855,12 @@ function toJSON(ast) {
1852
1855
  }
1853
1856
  }
1854
1857
  }
1855
- const validation = transformValidation(ast.validation, answers, widgets);
1856
1858
  return {
1857
1859
  version: "2.0",
1858
1860
  // Natural DSL version
1859
1861
  layout,
1860
1862
  widgets,
1861
- validation
1863
+ ...ast.validation ? { validation: transformValidation(ast.validation, answers, widgets) } : {}
1862
1864
  };
1863
1865
  }
1864
1866
  function collectAllInlines(blocks) {
@@ -2329,11 +2331,18 @@ function findValidCombination(expression, ranges) {
2329
2331
  return null;
2330
2332
  }
2331
2333
  function validate(exercise, userInputs) {
2332
- switch (exercise.validation.mode) {
2334
+ if (!exercise.validation) {
2335
+ return {
2336
+ isCorrect: true,
2337
+ details: {}
2338
+ };
2339
+ }
2340
+ const validatedExercise = exercise;
2341
+ switch (validatedExercise.validation.mode) {
2333
2342
  case VALIDATION_MODES.SIMPLE:
2334
- return validateSimple(exercise, userInputs);
2343
+ return validateSimple(validatedExercise, userInputs);
2335
2344
  case VALIDATION_MODES.EXPRESSION:
2336
- return validateExpression(exercise, userInputs);
2345
+ return validateExpression(validatedExercise, userInputs);
2337
2346
  default:
2338
2347
  return {
2339
2348
  isCorrect: false,
@@ -2344,6 +2353,9 @@ function validate(exercise, userInputs) {
2344
2353
  }
2345
2354
  function getMissingInputs(exercise, userInputs) {
2346
2355
  const missing = [];
2356
+ if (!exercise.validation) {
2357
+ return missing;
2358
+ }
2347
2359
  if (exercise.validation.mode === VALIDATION_MODES.SIMPLE && exercise.validation.answers) {
2348
2360
  for (const widgetId of Object.keys(exercise.validation.answers)) {
2349
2361
  if (!(widgetId in userInputs) || userInputs[widgetId] === "") {