@khanacademy/perseus 49.2.0 → 49.2.1

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/es/index.js CHANGED
@@ -32370,7 +32370,7 @@ const LegacyGrid = ({
32370
32370
  * "new-awesomeness": MyAwesomeNewWidget;
32371
32371
  *
32372
32372
  * // A deprecated widget
32373
- * "super-old-widget": AutoCorrectWidget;
32373
+ * "super-old-widget": DeprecatedStandinWidget;
32374
32374
  * }
32375
32375
  * }
32376
32376
  *
@@ -32564,46 +32564,28 @@ function LockedLabel(props) {
32564
32564
 
32565
32565
  function GraphLockedLabelsLayer(props) {
32566
32566
  const {
32567
- flags,
32568
32567
  lockedFigures
32569
32568
  } = props;
32570
32569
  return lockedFigures.map((figure, i) => {
32571
- var _flags$mafs, _flags$mafs2, _flags$mafs3, _flags$mafs4, _flags$mafs5, _flags$mafs6;
32570
+ var _figure$labels, _figure$points$0$labe, _figure$points$1$labe;
32572
32571
  if (figure.type === "label") {
32573
32572
  return /*#__PURE__*/React.createElement(LockedLabel, _extends({
32574
32573
  key: `label-${i}`
32575
32574
  }, figure));
32576
32575
  }
32577
- if (
32578
- // Point flag + point type
32579
- flags != null && (_flags$mafs = flags["mafs"]) != null && _flags$mafs["locked-point-labels"] && figure.type === "point" ||
32580
- // Line flag + line type
32581
- flags != null && (_flags$mafs2 = flags["mafs"]) != null && _flags$mafs2["locked-line-labels"] && figure.type === "line" ||
32582
- // Vector flag + vector type
32583
- flags != null && (_flags$mafs3 = flags["mafs"]) != null && _flags$mafs3["locked-vector-labels"] && figure.type === "vector" ||
32584
- // Ellipse flag + ellipse type
32585
- flags != null && (_flags$mafs4 = flags["mafs"]) != null && _flags$mafs4["locked-ellipse-labels"] && figure.type === "ellipse" ||
32586
- // Polygon flag + polygon type
32587
- flags != null && (_flags$mafs5 = flags["mafs"]) != null && _flags$mafs5["locked-polygon-labels"] && figure.type === "polygon" ||
32588
- // Function flag + function type
32589
- flags != null && (_flags$mafs6 = flags["mafs"]) != null && _flags$mafs6["locked-function-labels"] && figure.type === "function") {
32590
- var _figure$labels, _figure$points$0$labe, _figure$points$1$labe;
32591
- return /*#__PURE__*/React.createElement(React.Fragment, {
32592
- key: i
32593
- }, (_figure$labels = figure.labels) == null ? void 0 : _figure$labels.map((label, j) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
32594
- key: `${i}-label-${j}`
32595
- }, label))), figure.type === "line" && /*#__PURE__*/React.createElement(React.Fragment, null, (_figure$points$0$labe = figure.points[0].labels) == null ? void 0 : _figure$points$0$labe.map((label, k) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
32596
- key: `locked-figure-${i}-point-1-label-${k}`
32597
- }, label))), (_figure$points$1$labe = figure.points[1].labels) == null ? void 0 : _figure$points$1$labe.map((label, k) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
32598
- key: `locked-figure-${i}-point-2-label-${k}`
32599
- }, label)))));
32600
- }
32601
- return null;
32576
+ return /*#__PURE__*/React.createElement(React.Fragment, {
32577
+ key: i
32578
+ }, (_figure$labels = figure.labels) == null ? void 0 : _figure$labels.map((label, j) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
32579
+ key: `${i}-label-${j}`
32580
+ }, label))), figure.type === "line" && /*#__PURE__*/React.createElement(React.Fragment, null, (_figure$points$0$labe = figure.points[0].labels) == null ? void 0 : _figure$points$0$labe.map((label, k) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
32581
+ key: `locked-figure-${i}-point-1-label-${k}`
32582
+ }, label))), (_figure$points$1$labe = figure.points[1].labels) == null ? void 0 : _figure$points$1$labe.map((label, k) => /*#__PURE__*/React.createElement(LockedLabel, _extends({
32583
+ key: `locked-figure-${i}-point-2-label-${k}`
32584
+ }, label)))));
32602
32585
  });
32603
32586
  }
32604
32587
 
32605
32588
  const LockedEllipse = props => {
32606
- var _flags$mafs;
32607
32589
  const {
32608
32590
  center,
32609
32591
  radius,
@@ -32611,10 +32593,9 @@ const LockedEllipse = props => {
32611
32593
  color: color$1,
32612
32594
  fillStyle,
32613
32595
  strokeStyle,
32614
- ariaLabel,
32615
- flags
32596
+ ariaLabel
32616
32597
  } = props;
32617
- const hasAria = ariaLabel && (flags == null || (_flags$mafs = flags["mafs"]) == null ? void 0 : _flags$mafs["locked-figures-aria"]);
32598
+ const hasAria = !!ariaLabel;
32618
32599
  return /*#__PURE__*/React.createElement("g", {
32619
32600
  className: "locked-ellipse",
32620
32601
  "aria-label": hasAria ? ariaLabel : undefined,
@@ -32639,7 +32620,6 @@ const LockedEllipse = props => {
32639
32620
  };
32640
32621
 
32641
32622
  const LockedFunction = props => {
32642
- var _props$flags;
32643
32623
  const [equation, setEquation] = useState();
32644
32624
  const {
32645
32625
  color,
@@ -32652,7 +32632,7 @@ const LockedFunction = props => {
32652
32632
  style: strokeStyle,
32653
32633
  domain
32654
32634
  };
32655
- const hasAria = props.ariaLabel && ((_props$flags = props.flags) == null || (_props$flags = _props$flags["mafs"]) == null ? void 0 : _props$flags["locked-figures-aria"]);
32635
+ const hasAria = !!props.ariaLabel;
32656
32636
  useEffect(() => {
32657
32637
  // Parsing the equation in a "useEffect" hook saves about 2ms each frame
32658
32638
  // when the learner is interacting with the graph (i.e. moving points).
@@ -32768,7 +32748,6 @@ function getArrayWithoutDuplicates(array) {
32768
32748
  }
32769
32749
 
32770
32750
  const LockedLine = props => {
32771
- var _flags$mafs;
32772
32751
  const {
32773
32752
  color: color$1,
32774
32753
  lineStyle,
@@ -32777,11 +32756,10 @@ const LockedLine = props => {
32777
32756
  showPoint1,
32778
32757
  showPoint2,
32779
32758
  ariaLabel,
32780
- flags,
32781
32759
  range
32782
32760
  } = props;
32783
32761
  const [point1, point2] = points;
32784
- const hasAria = ariaLabel && (flags == null || (_flags$mafs = flags["mafs"]) == null ? void 0 : _flags$mafs["locked-figures-aria"]);
32762
+ const hasAria = !!ariaLabel;
32785
32763
  let line;
32786
32764
  if (kind === "ray") {
32787
32765
  // Rays extend to the end of the graph in one direction.
@@ -32848,16 +32826,14 @@ const LockedLine = props => {
32848
32826
  };
32849
32827
 
32850
32828
  const LockedPoint = props => {
32851
- var _flags$mafs;
32852
32829
  const {
32853
- flags,
32854
32830
  color: color$1,
32855
32831
  coord,
32856
32832
  filled,
32857
32833
  ariaLabel
32858
32834
  } = props;
32859
32835
  const [x, y] = coord;
32860
- const hasAria = ariaLabel && (flags == null || (_flags$mafs = flags["mafs"]) == null ? void 0 : _flags$mafs["locked-figures-aria"]);
32836
+ const hasAria = !!ariaLabel;
32861
32837
  return /*#__PURE__*/React.createElement("g", {
32862
32838
  className: "locked-point",
32863
32839
  "aria-label": hasAria ? ariaLabel : undefined,
@@ -32876,7 +32852,6 @@ const LockedPoint = props => {
32876
32852
  };
32877
32853
 
32878
32854
  const LockedPolygon = props => {
32879
- var _props$flags;
32880
32855
  const {
32881
32856
  points,
32882
32857
  color: color$1,
@@ -32884,7 +32859,7 @@ const LockedPolygon = props => {
32884
32859
  fillStyle,
32885
32860
  strokeStyle
32886
32861
  } = props;
32887
- const hasAria = props.ariaLabel && ((_props$flags = props.flags) == null || (_props$flags = _props$flags["mafs"]) == null ? void 0 : _props$flags["locked-figures-aria"]);
32862
+ const hasAria = !!props.ariaLabel;
32888
32863
  return /*#__PURE__*/React.createElement("g", {
32889
32864
  className: "locked-polygon",
32890
32865
  "aria-label": hasAria ? props.ariaLabel : undefined,
@@ -32912,15 +32887,13 @@ const LockedPolygon = props => {
32912
32887
  };
32913
32888
 
32914
32889
  const LockedVector = props => {
32915
- var _flags$mafs;
32916
32890
  const {
32917
32891
  color,
32918
32892
  points,
32919
- ariaLabel,
32920
- flags
32893
+ ariaLabel
32921
32894
  } = props;
32922
32895
  const [tail, tip] = points;
32923
- const hasAria = ariaLabel && (flags == null || (_flags$mafs = flags["mafs"]) == null ? void 0 : _flags$mafs["locked-figures-aria"]);
32896
+ const hasAria = !!ariaLabel;
32924
32897
  return /*#__PURE__*/React.createElement("g", {
32925
32898
  className: "locked-vector",
32926
32899
  "aria-label": hasAria ? ariaLabel : undefined,
@@ -32934,7 +32907,6 @@ const LockedVector = props => {
32934
32907
 
32935
32908
  const GraphLockedLayer = props => {
32936
32909
  const {
32937
- flags,
32938
32910
  lockedFigures
32939
32911
  } = props;
32940
32912
  return /*#__PURE__*/React.createElement(React.Fragment, null, lockedFigures.map((figure, index) => {
@@ -32942,40 +32914,28 @@ const GraphLockedLayer = props => {
32942
32914
  case "point":
32943
32915
  return /*#__PURE__*/React.createElement(LockedPoint, _extends({
32944
32916
  key: `point-${index}`
32945
- }, figure, {
32946
- flags: flags
32947
- }));
32917
+ }, figure));
32948
32918
  case "line":
32949
32919
  return /*#__PURE__*/React.createElement(LockedLine, _extends({
32950
32920
  key: `line-${index}`,
32951
32921
  range: props.range
32952
- }, figure, {
32953
- flags: flags
32954
- }));
32922
+ }, figure));
32955
32923
  case "vector":
32956
32924
  return /*#__PURE__*/React.createElement(LockedVector, _extends({
32957
32925
  key: `vector-${index}`
32958
- }, figure, {
32959
- flags: flags
32960
- }));
32926
+ }, figure));
32961
32927
  case "ellipse":
32962
32928
  return /*#__PURE__*/React.createElement(LockedEllipse, _extends({
32963
32929
  key: `ellipse-${index}`
32964
- }, figure, {
32965
- flags: flags
32966
- }));
32930
+ }, figure));
32967
32931
  case "polygon":
32968
32932
  return /*#__PURE__*/React.createElement(LockedPolygon, _extends({
32969
32933
  key: `polygon-${index}`
32970
- }, figure, {
32971
- flags: flags
32972
- }));
32934
+ }, figure));
32973
32935
  case "function":
32974
32936
  return /*#__PURE__*/React.createElement(LockedFunction, _extends({
32975
32937
  key: `function-${index}`
32976
- }, figure, {
32977
- flags: flags
32978
- }));
32938
+ }, figure));
32979
32939
  case "label":
32980
32940
  // This is rendered outside the SVG element, since
32981
32941
  // TeX cannot be rendered inside an SVG.
@@ -35446,7 +35406,6 @@ function useDraggablePx(args) {
35446
35406
  * - Interactive Graph Elements
35447
35407
  */
35448
35408
  const MafsGraph = props => {
35449
- var _props$flags;
35450
35409
  const {
35451
35410
  state,
35452
35411
  dispatch,
@@ -35583,11 +35542,9 @@ const MafsGraph = props => {
35583
35542
  })),
35584
35543
  // Only render the axis ticks and arrows if the markings are set to a full "graph"
35585
35544
  props.markings === "graph" && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(AxisTicks, null), /*#__PURE__*/React.createElement(AxisArrows, null)), /*#__PURE__*/React.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLayer, {
35586
- flags: props.flags,
35587
35545
  lockedFigures: props.lockedFigures,
35588
35546
  range: state.range
35589
- }))), ((_props$flags = props.flags) == null || (_props$flags = _props$flags["mafs"]) == null ? void 0 : _props$flags["interactive-graph-locked-features-labels"]) && props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
35590
- flags: props.flags,
35547
+ }))), props.lockedFigures && /*#__PURE__*/React.createElement(GraphLockedLabelsLayer, {
35591
35548
  lockedFigures: props.lockedFigures
35592
35549
  }), /*#__PURE__*/React.createElement(View, {
35593
35550
  style: {
@@ -45560,7 +45517,7 @@ const init = function init() {
45560
45517
 
45561
45518
  // This file is processed by a Rollup plugin (replace) to inject the production
45562
45519
  const libName = "@khanacademy/perseus";
45563
- const libVersion = "49.2.0";
45520
+ const libVersion = "49.2.1";
45564
45521
  addLibraryVersionToPerseusDebug(libName, libVersion);
45565
45522
 
45566
45523
  const apiVersion = {
@@ -47845,10 +47802,10 @@ class UnionBuilder {
47845
47802
  this.parser = parser;
47846
47803
  }
47847
47804
  or(newBranch) {
47848
- return new UnionBuilder(either$1(this.parser, newBranch));
47805
+ return new UnionBuilder(either(this.parser, newBranch));
47849
47806
  }
47850
47807
  }
47851
- function either$1(parseA, parseB) {
47808
+ function either(parseA, parseB) {
47852
47809
  return (rawValue, ctx) => {
47853
47810
  const resultA = parseA(rawValue, ctx);
47854
47811
  if (isSuccess(resultA)) {
@@ -47901,9 +47858,9 @@ function parseWidgetWithVersion(parseVersion, parseType, parseOptions) {
47901
47858
  const parseCategorizerWidget = parseWidget(constant("categorizer"), object({
47902
47859
  items: array(string),
47903
47860
  categories: array(string),
47904
- randomizeItems: boolean,
47861
+ randomizeItems: defaulted(boolean, () => false),
47905
47862
  static: defaulted(boolean, () => false),
47906
- values: array(number),
47863
+ values: array(defaulted(number, () => 0)),
47907
47864
  highlightLint: optional(boolean),
47908
47865
  linterContext: optional(object({
47909
47866
  contentType: string,
@@ -47923,7 +47880,7 @@ const parseCSProgramWidget = parseWidget(constant("cs-program"), object({
47923
47880
  showButtons: boolean,
47924
47881
  width: number,
47925
47882
  height: number,
47926
- static: boolean
47883
+ static: defaulted(boolean, () => false)
47927
47884
  }));
47928
47885
 
47929
47886
  const parseDefinitionWidget = parseWidget(constant("definition"), object({
@@ -47933,7 +47890,7 @@ const parseDefinitionWidget = parseWidget(constant("definition"), object({
47933
47890
  }));
47934
47891
 
47935
47892
  const parseDropdownWidget = parseWidget(constant("dropdown"), object({
47936
- placeholder: string,
47893
+ placeholder: defaulted(string, () => ""),
47937
47894
  ariaLabel: optional(string),
47938
47895
  visibleLabel: optional(string),
47939
47896
  static: defaulted(boolean, () => false),
@@ -47966,60 +47923,63 @@ function convert(f) {
47966
47923
  * data does not match any of the versions.
47967
47924
  *
47968
47925
  * @example
47969
- * const parseOptions = versionedWidgetOptions(parseOptionsV3)
47970
- * .withMigrationFrom(parseOptionsV2, migrateV2ToV3)
47971
- * .withMigrationFrom(parseOptionsV1, migrateV1ToV2)
47972
- * .withMigrationFrom(parseOptionsV0, migrateV0ToV1)
47926
+ * const parseOptions = versionedWidgetOptions(3, parseOptionsV3)
47927
+ * .withMigrationFrom(2, parseOptionsV2, migrateV2ToV3)
47928
+ * .withMigrationFrom(1, parseOptionsV1, migrateV1ToV2)
47929
+ * .withMigrationFrom(0, parseOptionsV0, migrateV0ToV1)
47973
47930
  * .parser;
47974
47931
  *
47932
+ * @param latestMajorVersion the latest major version of the widget options.
47975
47933
  * @param parseLatest a {@link Parser} for the latest version of the widget
47976
- * options. This should check version.major and fail if it's not the latest
47977
- * version.
47934
+ * options.
47978
47935
  * @returns a builder object, to which migrations from earlier versions can be
47979
47936
  * added. Migrations must be added in "reverse chronological" order as in the
47980
47937
  * example above.
47981
47938
  */
47982
- function versionedWidgetOptions(parseLatest) {
47983
- return new VersionedWidgetOptionsParserBuilder(parseLatest, latest => latest);
47939
+ function versionedWidgetOptions(latestMajorVersion, parseLatest) {
47940
+ return new VersionedWidgetOptionsParserBuilder(latestMajorVersion, parseLatest, latest => latest, (raw, ctx) => ctx.failure("widget options with a known version number", raw));
47984
47941
  }
47985
47942
  class VersionedWidgetOptionsParserBuilder {
47986
- constructor(parser, migrate) {
47987
- this.parser = parser;
47988
- this.migrate = migrate;
47943
+ constructor(majorVersion, parseThisVersion, migrateToLatest, parseOtherVersions) {
47944
+ this.parser = void 0;
47945
+ this.migrateToLatest = migrateToLatest;
47946
+ this.parseOtherVersions = parseOtherVersions;
47947
+ const parseThisVersionAndMigrateToLatest = pipeParsers(parseThisVersion).then(convert(this.migrateToLatest)).parser;
47948
+ this.parser = (raw, ctx) => {
47949
+ if (!isObject(raw)) {
47950
+ return ctx.failure("object", raw);
47951
+ }
47952
+ const versionParseResult = parseVersionedObject(raw, ctx);
47953
+ if (isFailure(versionParseResult)) {
47954
+ return versionParseResult;
47955
+ }
47956
+ if (versionParseResult.value.version.major !== majorVersion) {
47957
+ return this.parseOtherVersions(raw, ctx);
47958
+ }
47959
+ return parseThisVersionAndMigrateToLatest(raw, ctx);
47960
+ };
47989
47961
  }
47990
47962
 
47991
47963
  /**
47992
47964
  * Add a migration from an old version of the widget options.
47993
- *
47994
- * @returns a VersionedWidgetOptionsParserBuilder whose `parser` function
47995
- * is capable of migrating the old version to the latest version. The parser
47996
- * will always return the latest version of the widget options on a
47997
- * successful parse.
47998
- * @param parseOldVersion should be a {@link Parser} for the old options
47999
- * type. It should fail if version.major isn't correct.
48000
- * @param migrateToNextVersion should migrate the `Old` data to the
48001
- * `Migratable` version of the current VersionedWidgetOptionsParserBuilder.
48002
- * Usually, this means migrating to the next major version.
48003
47965
  */
48004
- withMigrationFrom(parseOldVersion, migrateToNextVersion) {
48005
- const parseLatestVersion = this.parser;
48006
- const migrateToLatest = old => this.migrate(migrateToNextVersion(old));
48007
- return new VersionedWidgetOptionsParserBuilder((raw, ctx) => {
48008
- const resultOfParsingLatest = parseLatestVersion(raw, ctx);
48009
- if (isSuccess(resultOfParsingLatest)) {
48010
- return resultOfParsingLatest;
48011
- }
48012
- const resultOfParsingOld = parseOldVersion(raw, ctx);
48013
- if (isSuccess(resultOfParsingOld)) {
48014
- return success(migrateToLatest(resultOfParsingOld.value));
48015
- }
48016
-
48017
- // If we're here, neither parse succeeded. Return the failure.
48018
- return resultOfParsingOld;
48019
- }, migrateToLatest);
47966
+ withMigrationFrom(majorVersion, parseOldVersion, migrateToNextVersion) {
47967
+ const parseOtherVersions = this.parser;
47968
+ const migrateToLatest = old => this.migrateToLatest(migrateToNextVersion(old));
47969
+ return new VersionedWidgetOptionsParserBuilder(majorVersion, parseOldVersion, migrateToLatest, parseOtherVersions);
48020
47970
  }
48021
47971
  }
47972
+ const parseVersionedObject = object({
47973
+ version: defaulted(object({
47974
+ major: number,
47975
+ minor: number
47976
+ }), () => ({
47977
+ major: 0,
47978
+ minor: 0
47979
+ }))
47980
+ });
48022
47981
 
47982
+ const stringOrNumberOrNullOrUndefined = union(string).or(number).or(constant(null)).or(constant(undefined)).parser;
48023
47983
  const parsePossiblyInvalidAnswerForm = object({
48024
47984
  // `value` is the possibly invalid part of this. It should always be a
48025
47985
  // string, but some answer forms don't have it. The Expression widget
@@ -48028,7 +47988,7 @@ const parsePossiblyInvalidAnswerForm = object({
48028
47988
  form: defaulted(boolean, () => false),
48029
47989
  simplify: defaulted(boolean, () => false),
48030
47990
  considered: enumeration("correct", "wrong", "ungraded"),
48031
- key: pipeParsers(optional(union(string).or(number).parser)).then((key, ctx) => ctx.success(String(key))).parser
47991
+ key: pipeParsers(stringOrNumberOrNullOrUndefined).then(convert(String)).parser
48032
47992
  });
48033
47993
  function removeInvalidAnswerForms(possiblyInvalid) {
48034
47994
  const valid = [];
@@ -48045,6 +48005,11 @@ function removeInvalidAnswerForms(possiblyInvalid) {
48045
48005
  }
48046
48006
  return valid;
48047
48007
  }
48008
+
48009
+ // NOTE(benchristel): I copied the default buttonSets from
48010
+ // expression.tsx. See the parse-perseus-json/README.md for
48011
+ // an explanation of why we want to duplicate the default here.
48012
+ const parseButtonSets = defaulted(array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")), () => ["basic", "trig", "prealgebra", "logarithms"]);
48048
48013
  const version1 = object({
48049
48014
  major: constant(1),
48050
48015
  minor: number
@@ -48055,7 +48020,7 @@ const parseExpressionWidgetV1 = parseWidgetWithVersion(version1, constant("expre
48055
48020
  times: boolean,
48056
48021
  visibleLabel: optional(string),
48057
48022
  ariaLabel: optional(string),
48058
- buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
48023
+ buttonSets: parseButtonSets,
48059
48024
  buttonsVisible: optional(enumeration("always", "never", "focused"))
48060
48025
  }));
48061
48026
  const version0 = optional(object({
@@ -48070,7 +48035,7 @@ const parseExpressionWidgetV0 = parseWidgetWithVersion(version0, constant("expre
48070
48035
  form: boolean,
48071
48036
  simplify: boolean,
48072
48037
  value: string,
48073
- buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
48038
+ buttonSets: parseButtonSets,
48074
48039
  buttonsVisible: optional(enumeration("always", "never", "focused"))
48075
48040
  }));
48076
48041
  function migrateV0ToV1(widget) {
@@ -48098,7 +48063,7 @@ function migrateV0ToV1(widget) {
48098
48063
  }
48099
48064
  });
48100
48065
  }
48101
- const parseExpressionWidget = versionedWidgetOptions(parseExpressionWidgetV1).withMigrationFrom(parseExpressionWidgetV0, migrateV0ToV1).parser;
48066
+ const parseExpressionWidget = versionedWidgetOptions(1, parseExpressionWidgetV1).withMigrationFrom(0, parseExpressionWidgetV0, migrateV0ToV1).parser;
48102
48067
 
48103
48068
  const parsePerseusRenderer = defaulted(object({
48104
48069
  // TODO(benchristel): content is also defaulted to empty string in
@@ -48119,13 +48084,14 @@ const parsePerseusRenderer = defaulted(object({
48119
48084
  images: {}
48120
48085
  }));
48121
48086
 
48087
+ const falseToNull = pipeParsers(constant(false)).then(convert(() => null)).parser;
48122
48088
  const parseGradedGroupWidgetOptions = object({
48123
48089
  title: defaulted(string, () => ""),
48124
48090
  hasHint: optional(nullable(boolean)),
48125
48091
  // This module has an import cycle with parsePerseusRenderer.
48126
48092
  // The anonymous function below ensures that we don't try to access
48127
48093
  // parsePerseusRenderer before it's defined.
48128
- hint: optional(nullable((rawVal, ctx) => parsePerseusRenderer(rawVal, ctx))),
48094
+ hint: union(falseToNull).or(constant(null)).or(constant(undefined)).or((rawVal, ctx) => parsePerseusRenderer(rawVal, ctx)).parser,
48129
48095
  content: string,
48130
48096
  // This module has an import cycle with parseWidgetsMap.
48131
48097
  // The anonymous function below ensures that we don't try to access
@@ -48144,28 +48110,41 @@ const parseGradedGroupSetWidget = parseWidget(constant("graded-group-set"), obje
48144
48110
  gradedGroups: array(parseGradedGroupWidgetOptions)
48145
48111
  }));
48146
48112
 
48147
- // discriminatedUnion() should be preferred over union() when parsing a
48148
- // discriminated union type, because discriminatedUnion() produces more
48149
- // understandable failure messages. It takes the discriminant as the source of
48150
- // truth for which variant is to be parsed, and expects the other data to match
48151
- // that variant.
48152
- function discriminatedUnion(narrow, parseVariant) {
48153
- return new DiscriminatedUnionBuilder(pipeParsers(narrow).then(parseVariant).parser);
48113
+ /**
48114
+ * discriminatedUnion() should be preferred over union() when parsing a
48115
+ * discriminated union type, because discriminatedUnion() produces more
48116
+ * understandable failure messages. It takes the discriminant as the source of
48117
+ * truth for which variant is to be parsed, and expects the other data to match
48118
+ * that variant.
48119
+ */
48120
+ function discriminatedUnionOn(discriminantKey) {
48121
+ const noMoreBranches = (raw, ctx) => {
48122
+ if (!isObject(raw)) {
48123
+ return ctx.failure("object", raw);
48124
+ }
48125
+ return ctx.forSubtree(discriminantKey).failure("a valid value", raw[discriminantKey]);
48126
+ };
48127
+ return new DiscriminatedUnionBuilder(discriminantKey, noMoreBranches);
48154
48128
  }
48155
48129
  class DiscriminatedUnionBuilder {
48156
- constructor(parser) {
48130
+ constructor(discriminantKey, parser) {
48131
+ this.discriminantKey = discriminantKey;
48157
48132
  this.parser = parser;
48158
48133
  }
48159
- or(narrow, parseVariant) {
48160
- return new DiscriminatedUnionBuilder(either(narrow, parseVariant, this.parser));
48134
+ withBranch(discriminantValue, parseNewVariant) {
48135
+ const parseNewBranch = discriminatedUnionBranch(this.discriminantKey, discriminantValue, parseNewVariant, this.parser);
48136
+ return new DiscriminatedUnionBuilder(this.discriminantKey, parseNewBranch);
48161
48137
  }
48162
48138
  }
48163
- function either(narrowToA, parseA, parseB) {
48164
- return (rawValue, ctx) => {
48165
- if (isSuccess(narrowToA(rawValue, ctx))) {
48166
- return parseA(rawValue, ctx);
48139
+ function discriminatedUnionBranch(discriminantKey, discriminantValue, parseVariant, parseOtherBranches) {
48140
+ return (raw, ctx) => {
48141
+ if (!isObject(raw)) {
48142
+ return ctx.failure("object", raw);
48167
48143
  }
48168
- return parseB(rawValue, ctx);
48144
+ if (raw[discriminantKey] === discriminantValue) {
48145
+ return parseVariant(raw, ctx);
48146
+ }
48147
+ return parseOtherBranches(raw, ctx);
48169
48148
  };
48170
48149
  }
48171
48150
 
@@ -48173,41 +48152,27 @@ const pairOfNumbers$3 = pair(number, number);
48173
48152
  const pairOfPoints = pair(pairOfNumbers$3, pairOfNumbers$3);
48174
48153
  const parseGrapherWidget = parseWidget(constant("grapher"), object({
48175
48154
  availableTypes: array(enumeration("absolute_value", "exponential", "linear", "logarithm", "quadratic", "sinusoid", "tangent")),
48176
- correct: discriminatedUnion(object({
48177
- type: constant("absolute_value")
48178
- }), object({
48155
+ correct: discriminatedUnionOn("type").withBranch("absolute_value", object({
48179
48156
  type: constant("absolute_value"),
48180
48157
  coords: pairOfPoints
48181
- })).or(object({
48182
- type: constant("exponential")
48183
- }), object({
48158
+ })).withBranch("exponential", object({
48184
48159
  type: constant("exponential"),
48185
48160
  asymptote: pairOfPoints,
48186
48161
  coords: pairOfPoints
48187
- })).or(object({
48188
- type: constant("linear")
48189
- }), object({
48162
+ })).withBranch("linear", object({
48190
48163
  type: constant("linear"),
48191
- coords: pairOfPoints
48192
- })).or(object({
48193
- type: constant("logarithm")
48194
- }), object({
48164
+ coords: defaulted(pairOfPoints, () => [[-5, 5], [5, 5]])
48165
+ })).withBranch("logarithm", object({
48195
48166
  type: constant("logarithm"),
48196
48167
  asymptote: pairOfPoints,
48197
48168
  coords: pairOfPoints
48198
- })).or(object({
48199
- type: constant("quadratic")
48200
- }), object({
48169
+ })).withBranch("quadratic", object({
48201
48170
  type: constant("quadratic"),
48202
48171
  coords: pairOfPoints
48203
- })).or(object({
48204
- type: constant("sinusoid")
48205
- }), object({
48172
+ })).withBranch("sinusoid", object({
48206
48173
  type: constant("sinusoid"),
48207
48174
  coords: pairOfPoints
48208
- })).or(object({
48209
- type: constant("tangent")
48210
- }), object({
48175
+ })).withBranch("tangent", object({
48211
48176
  type: constant("tangent"),
48212
48177
  coords: pairOfPoints
48213
48178
  })).parser,
@@ -48455,23 +48420,7 @@ const parseInteractionWidget = parseWidget(constant("interaction"), object({
48455
48420
  rulerTicks: optional(number),
48456
48421
  tickStep: pairOfNumbers$1
48457
48422
  }),
48458
- elements: array(discriminatedUnion(object({
48459
- type: parseFunctionType
48460
- }), parseFunctionElement).or(object({
48461
- type: parseLabelType
48462
- }), parseLabelElement).or(object({
48463
- type: parseLineType
48464
- }), parseLineElement).or(object({
48465
- type: parseMovableLineType
48466
- }), parseMovableLineElement).or(object({
48467
- type: parseMovablePointType
48468
- }), parseMovablePointElement).or(object({
48469
- type: parseParametricType
48470
- }), parseParametricElement).or(object({
48471
- type: parsePointType
48472
- }), parsePointElement).or(object({
48473
- type: parseRectangleType
48474
- }), parseRectangleElement).parser)
48423
+ elements: array(discriminatedUnionOn("type").withBranch("function", parseFunctionElement).withBranch("label", parseLabelElement).withBranch("line", parseLineElement).withBranch("movable-line", parseMovableLineElement).withBranch("movable-point", parseMovablePointElement).withBranch("parametric", parseParametricElement).withBranch("point", parsePointElement).withBranch("rectangle", parseRectangleElement).parser)
48475
48424
  }));
48476
48425
 
48477
48426
  // Used to represent 2-D points and ranges
@@ -48529,7 +48478,7 @@ const parsePerseusGraphTypePolygon = object({
48529
48478
  showAngles: optional(boolean),
48530
48479
  showSides: optional(boolean),
48531
48480
  snapTo: optional(enumeration("grid", "angles", "sides")),
48532
- match: optional(enumeration("similar", "congruent", "approx")),
48481
+ match: optional(enumeration("similar", "congruent", "approx", "exact")),
48533
48482
  startCoords: optional(array(pairOfNumbers)),
48534
48483
  // TODO: remove coord? it's legacy.
48535
48484
  coord: optional(pairOfNumbers)
@@ -48564,7 +48513,7 @@ const parsePerseusGraphTypeSinusoid = object({
48564
48513
  // TODO: remove coord? it's legacy.
48565
48514
  coord: optional(pairOfNumbers)
48566
48515
  });
48567
- const parsePerseusGraphType = union(parsePerseusGraphTypeAngle).or(parsePerseusGraphTypeAngle).or(parsePerseusGraphTypeCircle).or(parsePerseusGraphTypeLinear).or(parsePerseusGraphTypeLinearSystem).or(parsePerseusGraphTypeNone).or(parsePerseusGraphTypePoint).or(parsePerseusGraphTypePolygon).or(parsePerseusGraphTypeQuadratic).or(parsePerseusGraphTypeRay).or(parsePerseusGraphTypeSegment).or(parsePerseusGraphTypeSinusoid).parser;
48516
+ const parsePerseusGraphType = discriminatedUnionOn("type").withBranch("angle", parsePerseusGraphTypeAngle).withBranch("circle", parsePerseusGraphTypeCircle).withBranch("linear", parsePerseusGraphTypeLinear).withBranch("linear-system", parsePerseusGraphTypeLinearSystem).withBranch("none", parsePerseusGraphTypeNone).withBranch("point", parsePerseusGraphTypePoint).withBranch("polygon", parsePerseusGraphTypePolygon).withBranch("quadratic", parsePerseusGraphTypeQuadratic).withBranch("ray", parsePerseusGraphTypeRay).withBranch("segment", parsePerseusGraphTypeSegment).withBranch("sinusoid", parsePerseusGraphTypeSinusoid).parser;
48568
48517
  const parseLockedFigureColor = enumeration(...lockedFigureColorNames);
48569
48518
  const parseLockedFigureFillType = enumeration("none", "white", "translucent", "solid");
48570
48519
  const parseLockedLineStyle = enumeration("solid", "dashed");
@@ -48683,7 +48632,7 @@ const parseLabelImageWidget = parseWidget(constant("label-image"), object({
48683
48632
  })),
48684
48633
  hideChoicesFromInstructions: boolean,
48685
48634
  multipleAnswers: boolean,
48686
- static: boolean
48635
+ static: defaulted(boolean, () => false)
48687
48636
  }));
48688
48637
 
48689
48638
  const parseMatcherWidget = parseWidget(constant("matcher"), object({
@@ -48694,7 +48643,8 @@ const parseMatcherWidget = parseWidget(constant("matcher"), object({
48694
48643
  padding: boolean
48695
48644
  }));
48696
48645
 
48697
- const numeric = pipeParsers(union(number).or(string).parser).then(stringToNumber).parser;
48646
+ const numberOrString = union(number).or(string).parser;
48647
+ const numeric = pipeParsers(defaulted(numberOrString, () => NaN)).then(stringToNumber).parser;
48698
48648
  const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matrix"), object({
48699
48649
  prefix: optional(string),
48700
48650
  suffix: optional(string),
@@ -48713,7 +48663,8 @@ const parseMeasurerWidget = parseWidget(constant("measurer"), object({
48713
48663
  rulerPixels: number,
48714
48664
  rulerLength: number,
48715
48665
  box: pair(number, number),
48716
- static: boolean
48666
+ // TODO(benchristel): static is not used. Remove it?
48667
+ static: defaulted(boolean, () => false)
48717
48668
  }));
48718
48669
 
48719
48670
  const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), object({
@@ -48722,9 +48673,10 @@ const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), o
48722
48673
  smiles: optional(string)
48723
48674
  }));
48724
48675
 
48676
+ const emptyStringToNull = pipeParsers(constant("")).then(convert(() => null)).parser;
48725
48677
  const parseNumberLineWidget = parseWidget(constant("number-line"), object({
48726
48678
  range: array(number),
48727
- labelRange: array(nullable(number)),
48679
+ labelRange: array(nullable(union(number).or(emptyStringToNull).parser)),
48728
48680
  labelStyle: string,
48729
48681
  labelTicks: boolean,
48730
48682
  isTickCtrl: optional(nullable(boolean)),
@@ -48736,10 +48688,10 @@ const parseNumberLineWidget = parseWidget(constant("number-line"), object({
48736
48688
  snapDivisions: defaulted(number, () => 2),
48737
48689
  tickStep: optional(nullable(number)),
48738
48690
  correctRel: optional(nullable(string)),
48739
- correctX: number,
48691
+ correctX: nullable(number),
48740
48692
  initialX: optional(nullable(number)),
48741
48693
  showTooltip: optional(boolean),
48742
- static: boolean
48694
+ static: defaulted(boolean, () => false)
48743
48695
  }));
48744
48696
 
48745
48697
  const parseMathFormat = enumeration("integer", "mixed", "improper", "proper", "decimal", "percent", "pi");
@@ -48754,14 +48706,14 @@ const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
48754
48706
  answerForms: optional(array(parseMathFormat)),
48755
48707
  strict: boolean,
48756
48708
  maxError: optional(nullable(number)),
48757
- // TODO(benchristel): simplify should never be `true`, but we
48709
+ // TODO(benchristel): simplify should never be a boolean, but we
48758
48710
  // have some content where it is anyway. If we ever backfill
48759
48711
  // the data, we should simplify `simplify`.
48760
- simplify: optional(nullable(union(string).or(pipeParsers(constant(true)).then(convert(String)).parser).parser))
48712
+ simplify: optional(nullable(union(string).or(pipeParsers(boolean).then(convert(String)).parser).parser))
48761
48713
  })),
48762
48714
  labelText: optional(string),
48763
48715
  size: string,
48764
- coefficient: boolean,
48716
+ coefficient: defaulted(boolean, () => false),
48765
48717
  rightAlign: optional(boolean),
48766
48718
  static: defaulted(boolean, () => false),
48767
48719
  answerForms: optional(array(object({
@@ -48797,9 +48749,9 @@ const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
48797
48749
  }));
48798
48750
 
48799
48751
  const parsePassageWidget = parseWidget(constant("passage"), object({
48800
- footnotes: string,
48752
+ footnotes: defaulted(string, () => ""),
48801
48753
  passageText: string,
48802
- passageTitle: string,
48754
+ passageTitle: defaulted(string, () => ""),
48803
48755
  showLineNumbers: boolean,
48804
48756
  static: defaulted(boolean, () => false)
48805
48757
  }));
@@ -48983,6 +48935,10 @@ const parseWidgetsMapEntry = ([key, widget], widgetMap, ctx) => {
48983
48935
  return parseAndAssign(`sequence ${id}`, parseDeprecatedWidget);
48984
48936
  case "lights-puzzle":
48985
48937
  return parseAndAssign(`lights-puzzle ${id}`, parseDeprecatedWidget);
48938
+ case "simulator":
48939
+ return parseAndAssign(`simulator ${id}`, parseDeprecatedWidget);
48940
+ case "transformer":
48941
+ return parseAndAssign(`transformer ${id}`, parseDeprecatedWidget);
48986
48942
  default:
48987
48943
  if (getWidget(type)) {
48988
48944
  // @ts-expect-error - 'type' is not a valid widget type