@khanacademy/perseus 49.2.0 → 49.2.2

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.js CHANGED
@@ -32688,7 +32688,7 @@ const LegacyGrid = _ref => {
32688
32688
  * "new-awesomeness": MyAwesomeNewWidget;
32689
32689
  *
32690
32690
  * // A deprecated widget
32691
- * "super-old-widget": AutoCorrectWidget;
32691
+ * "super-old-widget": DeprecatedStandinWidget;
32692
32692
  * }
32693
32693
  * }
32694
32694
  *
@@ -32882,7 +32882,6 @@ function LockedLabel(props) {
32882
32882
 
32883
32883
  function GraphLockedLabelsLayer(props) {
32884
32884
  const {
32885
- flags,
32886
32885
  lockedFigures
32887
32886
  } = props;
32888
32887
  return lockedFigures.map((figure, i) => {
@@ -32891,30 +32890,15 @@ function GraphLockedLabelsLayer(props) {
32891
32890
  key: `label-${i}`
32892
32891
  }, figure));
32893
32892
  }
32894
- if (
32895
- // Point flag + point type
32896
- flags?.["mafs"]?.["locked-point-labels"] && figure.type === "point" ||
32897
- // Line flag + line type
32898
- flags?.["mafs"]?.["locked-line-labels"] && figure.type === "line" ||
32899
- // Vector flag + vector type
32900
- flags?.["mafs"]?.["locked-vector-labels"] && figure.type === "vector" ||
32901
- // Ellipse flag + ellipse type
32902
- flags?.["mafs"]?.["locked-ellipse-labels"] && figure.type === "ellipse" ||
32903
- // Polygon flag + polygon type
32904
- flags?.["mafs"]?.["locked-polygon-labels"] && figure.type === "polygon" ||
32905
- // Function flag + function type
32906
- flags?.["mafs"]?.["locked-function-labels"] && figure.type === "function") {
32907
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, {
32908
- key: i
32909
- }, figure.labels?.map((label, j) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
32910
- key: `${i}-label-${j}`
32911
- }, label))), figure.type === "line" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, figure.points[0].labels?.map((label, k) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
32912
- key: `locked-figure-${i}-point-1-label-${k}`
32913
- }, label))), figure.points[1].labels?.map((label, k) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
32914
- key: `locked-figure-${i}-point-2-label-${k}`
32915
- }, label)))));
32916
- }
32917
- return null;
32893
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, {
32894
+ key: i
32895
+ }, figure.labels?.map((label, j) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
32896
+ key: `${i}-label-${j}`
32897
+ }, label))), figure.type === "line" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, figure.points[0].labels?.map((label, k) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
32898
+ key: `locked-figure-${i}-point-1-label-${k}`
32899
+ }, label))), figure.points[1].labels?.map((label, k) => /*#__PURE__*/React__namespace.createElement(LockedLabel, _extends({
32900
+ key: `locked-figure-${i}-point-2-label-${k}`
32901
+ }, label)))));
32918
32902
  });
32919
32903
  }
32920
32904
 
@@ -32926,10 +32910,9 @@ const LockedEllipse = props => {
32926
32910
  color,
32927
32911
  fillStyle,
32928
32912
  strokeStyle,
32929
- ariaLabel,
32930
- flags
32913
+ ariaLabel
32931
32914
  } = props;
32932
- const hasAria = ariaLabel && flags?.["mafs"]?.["locked-figures-aria"];
32915
+ const hasAria = !!ariaLabel;
32933
32916
  return /*#__PURE__*/React__namespace.createElement("g", {
32934
32917
  className: "locked-ellipse",
32935
32918
  "aria-label": hasAria ? ariaLabel : undefined,
@@ -32966,7 +32949,7 @@ const LockedFunction = props => {
32966
32949
  style: strokeStyle,
32967
32950
  domain
32968
32951
  };
32969
- const hasAria = props.ariaLabel && props.flags?.["mafs"]?.["locked-figures-aria"];
32952
+ const hasAria = !!props.ariaLabel;
32970
32953
  React.useEffect(() => {
32971
32954
  // Parsing the equation in a "useEffect" hook saves about 2ms each frame
32972
32955
  // when the learner is interacting with the graph (i.e. moving points).
@@ -33093,11 +33076,10 @@ const LockedLine = props => {
33093
33076
  showPoint1,
33094
33077
  showPoint2,
33095
33078
  ariaLabel,
33096
- flags,
33097
33079
  range
33098
33080
  } = props;
33099
33081
  const [point1, point2] = points;
33100
- const hasAria = ariaLabel && flags?.["mafs"]?.["locked-figures-aria"];
33082
+ const hasAria = !!ariaLabel;
33101
33083
  let line;
33102
33084
  if (kind === "ray") {
33103
33085
  // Rays extend to the end of the graph in one direction.
@@ -33165,14 +33147,13 @@ const LockedLine = props => {
33165
33147
 
33166
33148
  const LockedPoint = props => {
33167
33149
  const {
33168
- flags,
33169
33150
  color,
33170
33151
  coord,
33171
33152
  filled,
33172
33153
  ariaLabel
33173
33154
  } = props;
33174
33155
  const [x, y] = coord;
33175
- const hasAria = ariaLabel && flags?.["mafs"]?.["locked-figures-aria"];
33156
+ const hasAria = !!ariaLabel;
33176
33157
  return /*#__PURE__*/React__namespace.createElement("g", {
33177
33158
  className: "locked-point",
33178
33159
  "aria-label": hasAria ? ariaLabel : undefined,
@@ -33198,7 +33179,7 @@ const LockedPolygon = props => {
33198
33179
  fillStyle,
33199
33180
  strokeStyle
33200
33181
  } = props;
33201
- const hasAria = props.ariaLabel && props.flags?.["mafs"]?.["locked-figures-aria"];
33182
+ const hasAria = !!props.ariaLabel;
33202
33183
  return /*#__PURE__*/React__namespace.createElement("g", {
33203
33184
  className: "locked-polygon",
33204
33185
  "aria-label": hasAria ? props.ariaLabel : undefined,
@@ -33229,11 +33210,10 @@ const LockedVector = props => {
33229
33210
  const {
33230
33211
  color,
33231
33212
  points,
33232
- ariaLabel,
33233
- flags
33213
+ ariaLabel
33234
33214
  } = props;
33235
33215
  const [tail, tip] = points;
33236
- const hasAria = ariaLabel && flags?.["mafs"]?.["locked-figures-aria"];
33216
+ const hasAria = !!ariaLabel;
33237
33217
  return /*#__PURE__*/React__namespace.createElement("g", {
33238
33218
  className: "locked-vector",
33239
33219
  "aria-label": hasAria ? ariaLabel : undefined,
@@ -33247,7 +33227,6 @@ const LockedVector = props => {
33247
33227
 
33248
33228
  const GraphLockedLayer = props => {
33249
33229
  const {
33250
- flags,
33251
33230
  lockedFigures
33252
33231
  } = props;
33253
33232
  return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, lockedFigures.map((figure, index) => {
@@ -33255,40 +33234,28 @@ const GraphLockedLayer = props => {
33255
33234
  case "point":
33256
33235
  return /*#__PURE__*/React__namespace.createElement(LockedPoint, _extends({
33257
33236
  key: `point-${index}`
33258
- }, figure, {
33259
- flags: flags
33260
- }));
33237
+ }, figure));
33261
33238
  case "line":
33262
33239
  return /*#__PURE__*/React__namespace.createElement(LockedLine, _extends({
33263
33240
  key: `line-${index}`,
33264
33241
  range: props.range
33265
- }, figure, {
33266
- flags: flags
33267
- }));
33242
+ }, figure));
33268
33243
  case "vector":
33269
33244
  return /*#__PURE__*/React__namespace.createElement(LockedVector, _extends({
33270
33245
  key: `vector-${index}`
33271
- }, figure, {
33272
- flags: flags
33273
- }));
33246
+ }, figure));
33274
33247
  case "ellipse":
33275
33248
  return /*#__PURE__*/React__namespace.createElement(LockedEllipse, _extends({
33276
33249
  key: `ellipse-${index}`
33277
- }, figure, {
33278
- flags: flags
33279
- }));
33250
+ }, figure));
33280
33251
  case "polygon":
33281
33252
  return /*#__PURE__*/React__namespace.createElement(LockedPolygon, _extends({
33282
33253
  key: `polygon-${index}`
33283
- }, figure, {
33284
- flags: flags
33285
- }));
33254
+ }, figure));
33286
33255
  case "function":
33287
33256
  return /*#__PURE__*/React__namespace.createElement(LockedFunction, _extends({
33288
33257
  key: `function-${index}`
33289
- }, figure, {
33290
- flags: flags
33291
- }));
33258
+ }, figure));
33292
33259
  case "label":
33293
33260
  // This is rendered outside the SVG element, since
33294
33261
  // TeX cannot be rendered inside an SVG.
@@ -35930,11 +35897,9 @@ const MafsGraph = props => {
35930
35897
  })),
35931
35898
  // Only render the axis ticks and arrows if the markings are set to a full "graph"
35932
35899
  props.markings === "graph" && /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(AxisTicks, null), /*#__PURE__*/React__namespace.createElement(AxisArrows, null)), /*#__PURE__*/React__namespace.createElement("svg", nestedSVGAttributes, props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLayer, {
35933
- flags: props.flags,
35934
35900
  lockedFigures: props.lockedFigures,
35935
35901
  range: state.range
35936
- }))), props.flags?.["mafs"]?.["interactive-graph-locked-features-labels"] && props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
35937
- flags: props.flags,
35902
+ }))), props.lockedFigures && /*#__PURE__*/React__namespace.createElement(GraphLockedLabelsLayer, {
35938
35903
  lockedFigures: props.lockedFigures
35939
35904
  }), /*#__PURE__*/React__namespace.createElement(wonderBlocksCore.View, {
35940
35905
  style: {
@@ -45944,7 +45909,7 @@ const init = function () {
45944
45909
 
45945
45910
  // This file is processed by a Rollup plugin (replace) to inject the production
45946
45911
  const libName = "@khanacademy/perseus";
45947
- const libVersion = "49.2.0";
45912
+ const libVersion = "49.2.2";
45948
45913
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
45949
45914
 
45950
45915
  const apiVersion = {
@@ -48244,10 +48209,10 @@ class UnionBuilder {
48244
48209
  this.parser = parser;
48245
48210
  }
48246
48211
  or(newBranch) {
48247
- return new UnionBuilder(either$1(this.parser, newBranch));
48212
+ return new UnionBuilder(either(this.parser, newBranch));
48248
48213
  }
48249
48214
  }
48250
- function either$1(parseA, parseB) {
48215
+ function either(parseA, parseB) {
48251
48216
  return (rawValue, ctx) => {
48252
48217
  const resultA = parseA(rawValue, ctx);
48253
48218
  if (isSuccess(resultA)) {
@@ -48300,9 +48265,9 @@ function parseWidgetWithVersion(parseVersion, parseType, parseOptions) {
48300
48265
  const parseCategorizerWidget = parseWidget(constant("categorizer"), object({
48301
48266
  items: array(string),
48302
48267
  categories: array(string),
48303
- randomizeItems: boolean,
48268
+ randomizeItems: defaulted(boolean, () => false),
48304
48269
  static: defaulted(boolean, () => false),
48305
- values: array(number),
48270
+ values: array(defaulted(number, () => 0)),
48306
48271
  highlightLint: optional(boolean),
48307
48272
  linterContext: optional(object({
48308
48273
  contentType: string,
@@ -48322,7 +48287,7 @@ const parseCSProgramWidget = parseWidget(constant("cs-program"), object({
48322
48287
  showButtons: boolean,
48323
48288
  width: number,
48324
48289
  height: number,
48325
- static: boolean
48290
+ static: defaulted(boolean, () => false)
48326
48291
  }));
48327
48292
 
48328
48293
  const parseDefinitionWidget = parseWidget(constant("definition"), object({
@@ -48332,7 +48297,7 @@ const parseDefinitionWidget = parseWidget(constant("definition"), object({
48332
48297
  }));
48333
48298
 
48334
48299
  const parseDropdownWidget = parseWidget(constant("dropdown"), object({
48335
- placeholder: string,
48300
+ placeholder: defaulted(string, () => ""),
48336
48301
  ariaLabel: optional(string),
48337
48302
  visibleLabel: optional(string),
48338
48303
  static: defaulted(boolean, () => false),
@@ -48365,60 +48330,63 @@ function convert(f) {
48365
48330
  * data does not match any of the versions.
48366
48331
  *
48367
48332
  * @example
48368
- * const parseOptions = versionedWidgetOptions(parseOptionsV3)
48369
- * .withMigrationFrom(parseOptionsV2, migrateV2ToV3)
48370
- * .withMigrationFrom(parseOptionsV1, migrateV1ToV2)
48371
- * .withMigrationFrom(parseOptionsV0, migrateV0ToV1)
48333
+ * const parseOptions = versionedWidgetOptions(3, parseOptionsV3)
48334
+ * .withMigrationFrom(2, parseOptionsV2, migrateV2ToV3)
48335
+ * .withMigrationFrom(1, parseOptionsV1, migrateV1ToV2)
48336
+ * .withMigrationFrom(0, parseOptionsV0, migrateV0ToV1)
48372
48337
  * .parser;
48373
48338
  *
48339
+ * @param latestMajorVersion the latest major version of the widget options.
48374
48340
  * @param parseLatest a {@link Parser} for the latest version of the widget
48375
- * options. This should check version.major and fail if it's not the latest
48376
- * version.
48341
+ * options.
48377
48342
  * @returns a builder object, to which migrations from earlier versions can be
48378
48343
  * added. Migrations must be added in "reverse chronological" order as in the
48379
48344
  * example above.
48380
48345
  */
48381
- function versionedWidgetOptions(parseLatest) {
48382
- return new VersionedWidgetOptionsParserBuilder(parseLatest, latest => latest);
48346
+ function versionedWidgetOptions(latestMajorVersion, parseLatest) {
48347
+ return new VersionedWidgetOptionsParserBuilder(latestMajorVersion, parseLatest, latest => latest, (raw, ctx) => ctx.failure("widget options with a known version number", raw));
48383
48348
  }
48384
48349
  class VersionedWidgetOptionsParserBuilder {
48385
- constructor(parser, migrate) {
48386
- this.parser = parser;
48387
- this.migrate = migrate;
48350
+ parser;
48351
+ constructor(majorVersion, parseThisVersion, migrateToLatest, parseOtherVersions) {
48352
+ this.migrateToLatest = migrateToLatest;
48353
+ this.parseOtherVersions = parseOtherVersions;
48354
+ const parseThisVersionAndMigrateToLatest = pipeParsers(parseThisVersion).then(convert(this.migrateToLatest)).parser;
48355
+ this.parser = (raw, ctx) => {
48356
+ if (!isObject(raw)) {
48357
+ return ctx.failure("object", raw);
48358
+ }
48359
+ const versionParseResult = parseVersionedObject(raw, ctx);
48360
+ if (isFailure(versionParseResult)) {
48361
+ return versionParseResult;
48362
+ }
48363
+ if (versionParseResult.value.version.major !== majorVersion) {
48364
+ return this.parseOtherVersions(raw, ctx);
48365
+ }
48366
+ return parseThisVersionAndMigrateToLatest(raw, ctx);
48367
+ };
48388
48368
  }
48389
48369
 
48390
48370
  /**
48391
48371
  * Add a migration from an old version of the widget options.
48392
- *
48393
- * @returns a VersionedWidgetOptionsParserBuilder whose `parser` function
48394
- * is capable of migrating the old version to the latest version. The parser
48395
- * will always return the latest version of the widget options on a
48396
- * successful parse.
48397
- * @param parseOldVersion should be a {@link Parser} for the old options
48398
- * type. It should fail if version.major isn't correct.
48399
- * @param migrateToNextVersion should migrate the `Old` data to the
48400
- * `Migratable` version of the current VersionedWidgetOptionsParserBuilder.
48401
- * Usually, this means migrating to the next major version.
48402
48372
  */
48403
- withMigrationFrom(parseOldVersion, migrateToNextVersion) {
48404
- const parseLatestVersion = this.parser;
48405
- const migrateToLatest = old => this.migrate(migrateToNextVersion(old));
48406
- return new VersionedWidgetOptionsParserBuilder((raw, ctx) => {
48407
- const resultOfParsingLatest = parseLatestVersion(raw, ctx);
48408
- if (isSuccess(resultOfParsingLatest)) {
48409
- return resultOfParsingLatest;
48410
- }
48411
- const resultOfParsingOld = parseOldVersion(raw, ctx);
48412
- if (isSuccess(resultOfParsingOld)) {
48413
- return success(migrateToLatest(resultOfParsingOld.value));
48414
- }
48415
-
48416
- // If we're here, neither parse succeeded. Return the failure.
48417
- return resultOfParsingOld;
48418
- }, migrateToLatest);
48373
+ withMigrationFrom(majorVersion, parseOldVersion, migrateToNextVersion) {
48374
+ const parseOtherVersions = this.parser;
48375
+ const migrateToLatest = old => this.migrateToLatest(migrateToNextVersion(old));
48376
+ return new VersionedWidgetOptionsParserBuilder(majorVersion, parseOldVersion, migrateToLatest, parseOtherVersions);
48419
48377
  }
48420
48378
  }
48379
+ const parseVersionedObject = object({
48380
+ version: defaulted(object({
48381
+ major: number,
48382
+ minor: number
48383
+ }), () => ({
48384
+ major: 0,
48385
+ minor: 0
48386
+ }))
48387
+ });
48421
48388
 
48389
+ const stringOrNumberOrNullOrUndefined = union(string).or(number).or(constant(null)).or(constant(undefined)).parser;
48422
48390
  const parsePossiblyInvalidAnswerForm = object({
48423
48391
  // `value` is the possibly invalid part of this. It should always be a
48424
48392
  // string, but some answer forms don't have it. The Expression widget
@@ -48427,7 +48395,7 @@ const parsePossiblyInvalidAnswerForm = object({
48427
48395
  form: defaulted(boolean, () => false),
48428
48396
  simplify: defaulted(boolean, () => false),
48429
48397
  considered: enumeration("correct", "wrong", "ungraded"),
48430
- key: pipeParsers(optional(union(string).or(number).parser)).then((key, ctx) => ctx.success(String(key))).parser
48398
+ key: pipeParsers(stringOrNumberOrNullOrUndefined).then(convert(String)).parser
48431
48399
  });
48432
48400
  function removeInvalidAnswerForms(possiblyInvalid) {
48433
48401
  const valid = [];
@@ -48445,6 +48413,11 @@ function removeInvalidAnswerForms(possiblyInvalid) {
48445
48413
  }
48446
48414
  return valid;
48447
48415
  }
48416
+
48417
+ // NOTE(benchristel): I copied the default buttonSets from
48418
+ // expression.tsx. See the parse-perseus-json/README.md for
48419
+ // an explanation of why we want to duplicate the default here.
48420
+ const parseButtonSets = defaulted(array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")), () => ["basic", "trig", "prealgebra", "logarithms"]);
48448
48421
  const version1 = object({
48449
48422
  major: constant(1),
48450
48423
  minor: number
@@ -48455,7 +48428,7 @@ const parseExpressionWidgetV1 = parseWidgetWithVersion(version1, constant("expre
48455
48428
  times: boolean,
48456
48429
  visibleLabel: optional(string),
48457
48430
  ariaLabel: optional(string),
48458
- buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
48431
+ buttonSets: parseButtonSets,
48459
48432
  buttonsVisible: optional(enumeration("always", "never", "focused"))
48460
48433
  }));
48461
48434
  const version0 = optional(object({
@@ -48470,7 +48443,7 @@ const parseExpressionWidgetV0 = parseWidgetWithVersion(version0, constant("expre
48470
48443
  form: boolean,
48471
48444
  simplify: boolean,
48472
48445
  value: string,
48473
- buttonSets: array(enumeration("basic", "basic+div", "trig", "prealgebra", "logarithms", "basic relations", "advanced relations")),
48446
+ buttonSets: parseButtonSets,
48474
48447
  buttonsVisible: optional(enumeration("always", "never", "focused"))
48475
48448
  }));
48476
48449
  function migrateV0ToV1(widget) {
@@ -48499,7 +48472,7 @@ function migrateV0ToV1(widget) {
48499
48472
  }
48500
48473
  };
48501
48474
  }
48502
- const parseExpressionWidget = versionedWidgetOptions(parseExpressionWidgetV1).withMigrationFrom(parseExpressionWidgetV0, migrateV0ToV1).parser;
48475
+ const parseExpressionWidget = versionedWidgetOptions(1, parseExpressionWidgetV1).withMigrationFrom(0, parseExpressionWidgetV0, migrateV0ToV1).parser;
48503
48476
 
48504
48477
  const parsePerseusRenderer = defaulted(object({
48505
48478
  // TODO(benchristel): content is also defaulted to empty string in
@@ -48520,13 +48493,14 @@ const parsePerseusRenderer = defaulted(object({
48520
48493
  images: {}
48521
48494
  }));
48522
48495
 
48496
+ const falseToNull = pipeParsers(constant(false)).then(convert(() => null)).parser;
48523
48497
  const parseGradedGroupWidgetOptions = object({
48524
48498
  title: defaulted(string, () => ""),
48525
48499
  hasHint: optional(nullable(boolean)),
48526
48500
  // This module has an import cycle with parsePerseusRenderer.
48527
48501
  // The anonymous function below ensures that we don't try to access
48528
48502
  // parsePerseusRenderer before it's defined.
48529
- hint: optional(nullable((rawVal, ctx) => parsePerseusRenderer(rawVal, ctx))),
48503
+ hint: union(falseToNull).or(constant(null)).or(constant(undefined)).or((rawVal, ctx) => parsePerseusRenderer(rawVal, ctx)).parser,
48530
48504
  content: string,
48531
48505
  // This module has an import cycle with parseWidgetsMap.
48532
48506
  // The anonymous function below ensures that we don't try to access
@@ -48545,28 +48519,41 @@ const parseGradedGroupSetWidget = parseWidget(constant("graded-group-set"), obje
48545
48519
  gradedGroups: array(parseGradedGroupWidgetOptions)
48546
48520
  }));
48547
48521
 
48548
- // discriminatedUnion() should be preferred over union() when parsing a
48549
- // discriminated union type, because discriminatedUnion() produces more
48550
- // understandable failure messages. It takes the discriminant as the source of
48551
- // truth for which variant is to be parsed, and expects the other data to match
48552
- // that variant.
48553
- function discriminatedUnion(narrow, parseVariant) {
48554
- return new DiscriminatedUnionBuilder(pipeParsers(narrow).then(parseVariant).parser);
48522
+ /**
48523
+ * discriminatedUnion() should be preferred over union() when parsing a
48524
+ * discriminated union type, because discriminatedUnion() produces more
48525
+ * understandable failure messages. It takes the discriminant as the source of
48526
+ * truth for which variant is to be parsed, and expects the other data to match
48527
+ * that variant.
48528
+ */
48529
+ function discriminatedUnionOn(discriminantKey) {
48530
+ const noMoreBranches = (raw, ctx) => {
48531
+ if (!isObject(raw)) {
48532
+ return ctx.failure("object", raw);
48533
+ }
48534
+ return ctx.forSubtree(discriminantKey).failure("a valid value", raw[discriminantKey]);
48535
+ };
48536
+ return new DiscriminatedUnionBuilder(discriminantKey, noMoreBranches);
48555
48537
  }
48556
48538
  class DiscriminatedUnionBuilder {
48557
- constructor(parser) {
48539
+ constructor(discriminantKey, parser) {
48540
+ this.discriminantKey = discriminantKey;
48558
48541
  this.parser = parser;
48559
48542
  }
48560
- or(narrow, parseVariant) {
48561
- return new DiscriminatedUnionBuilder(either(narrow, parseVariant, this.parser));
48543
+ withBranch(discriminantValue, parseNewVariant) {
48544
+ const parseNewBranch = discriminatedUnionBranch(this.discriminantKey, discriminantValue, parseNewVariant, this.parser);
48545
+ return new DiscriminatedUnionBuilder(this.discriminantKey, parseNewBranch);
48562
48546
  }
48563
48547
  }
48564
- function either(narrowToA, parseA, parseB) {
48565
- return (rawValue, ctx) => {
48566
- if (isSuccess(narrowToA(rawValue, ctx))) {
48567
- return parseA(rawValue, ctx);
48548
+ function discriminatedUnionBranch(discriminantKey, discriminantValue, parseVariant, parseOtherBranches) {
48549
+ return (raw, ctx) => {
48550
+ if (!isObject(raw)) {
48551
+ return ctx.failure("object", raw);
48568
48552
  }
48569
- return parseB(rawValue, ctx);
48553
+ if (raw[discriminantKey] === discriminantValue) {
48554
+ return parseVariant(raw, ctx);
48555
+ }
48556
+ return parseOtherBranches(raw, ctx);
48570
48557
  };
48571
48558
  }
48572
48559
 
@@ -48574,41 +48561,27 @@ const pairOfNumbers$3 = pair(number, number);
48574
48561
  const pairOfPoints = pair(pairOfNumbers$3, pairOfNumbers$3);
48575
48562
  const parseGrapherWidget = parseWidget(constant("grapher"), object({
48576
48563
  availableTypes: array(enumeration("absolute_value", "exponential", "linear", "logarithm", "quadratic", "sinusoid", "tangent")),
48577
- correct: discriminatedUnion(object({
48578
- type: constant("absolute_value")
48579
- }), object({
48564
+ correct: discriminatedUnionOn("type").withBranch("absolute_value", object({
48580
48565
  type: constant("absolute_value"),
48581
48566
  coords: pairOfPoints
48582
- })).or(object({
48583
- type: constant("exponential")
48584
- }), object({
48567
+ })).withBranch("exponential", object({
48585
48568
  type: constant("exponential"),
48586
48569
  asymptote: pairOfPoints,
48587
48570
  coords: pairOfPoints
48588
- })).or(object({
48589
- type: constant("linear")
48590
- }), object({
48571
+ })).withBranch("linear", object({
48591
48572
  type: constant("linear"),
48592
- coords: pairOfPoints
48593
- })).or(object({
48594
- type: constant("logarithm")
48595
- }), object({
48573
+ coords: defaulted(pairOfPoints, () => [[-5, 5], [5, 5]])
48574
+ })).withBranch("logarithm", object({
48596
48575
  type: constant("logarithm"),
48597
48576
  asymptote: pairOfPoints,
48598
48577
  coords: pairOfPoints
48599
- })).or(object({
48600
- type: constant("quadratic")
48601
- }), object({
48578
+ })).withBranch("quadratic", object({
48602
48579
  type: constant("quadratic"),
48603
48580
  coords: pairOfPoints
48604
- })).or(object({
48605
- type: constant("sinusoid")
48606
- }), object({
48581
+ })).withBranch("sinusoid", object({
48607
48582
  type: constant("sinusoid"),
48608
48583
  coords: pairOfPoints
48609
- })).or(object({
48610
- type: constant("tangent")
48611
- }), object({
48584
+ })).withBranch("tangent", object({
48612
48585
  type: constant("tangent"),
48613
48586
  coords: pairOfPoints
48614
48587
  })).parser,
@@ -48856,23 +48829,7 @@ const parseInteractionWidget = parseWidget(constant("interaction"), object({
48856
48829
  rulerTicks: optional(number),
48857
48830
  tickStep: pairOfNumbers$1
48858
48831
  }),
48859
- elements: array(discriminatedUnion(object({
48860
- type: parseFunctionType
48861
- }), parseFunctionElement).or(object({
48862
- type: parseLabelType
48863
- }), parseLabelElement).or(object({
48864
- type: parseLineType
48865
- }), parseLineElement).or(object({
48866
- type: parseMovableLineType
48867
- }), parseMovableLineElement).or(object({
48868
- type: parseMovablePointType
48869
- }), parseMovablePointElement).or(object({
48870
- type: parseParametricType
48871
- }), parseParametricElement).or(object({
48872
- type: parsePointType
48873
- }), parsePointElement).or(object({
48874
- type: parseRectangleType
48875
- }), parseRectangleElement).parser)
48832
+ 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)
48876
48833
  }));
48877
48834
 
48878
48835
  // Used to represent 2-D points and ranges
@@ -48930,7 +48887,7 @@ const parsePerseusGraphTypePolygon = object({
48930
48887
  showAngles: optional(boolean),
48931
48888
  showSides: optional(boolean),
48932
48889
  snapTo: optional(enumeration("grid", "angles", "sides")),
48933
- match: optional(enumeration("similar", "congruent", "approx")),
48890
+ match: optional(enumeration("similar", "congruent", "approx", "exact")),
48934
48891
  startCoords: optional(array(pairOfNumbers)),
48935
48892
  // TODO: remove coord? it's legacy.
48936
48893
  coord: optional(pairOfNumbers)
@@ -48965,7 +48922,7 @@ const parsePerseusGraphTypeSinusoid = object({
48965
48922
  // TODO: remove coord? it's legacy.
48966
48923
  coord: optional(pairOfNumbers)
48967
48924
  });
48968
- 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;
48925
+ 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;
48969
48926
  const parseLockedFigureColor = enumeration(...lockedFigureColorNames);
48970
48927
  const parseLockedFigureFillType = enumeration("none", "white", "translucent", "solid");
48971
48928
  const parseLockedLineStyle = enumeration("solid", "dashed");
@@ -49084,7 +49041,7 @@ const parseLabelImageWidget = parseWidget(constant("label-image"), object({
49084
49041
  })),
49085
49042
  hideChoicesFromInstructions: boolean,
49086
49043
  multipleAnswers: boolean,
49087
- static: boolean
49044
+ static: defaulted(boolean, () => false)
49088
49045
  }));
49089
49046
 
49090
49047
  const parseMatcherWidget = parseWidget(constant("matcher"), object({
@@ -49095,7 +49052,8 @@ const parseMatcherWidget = parseWidget(constant("matcher"), object({
49095
49052
  padding: boolean
49096
49053
  }));
49097
49054
 
49098
- const numeric = pipeParsers(union(number).or(string).parser).then(stringToNumber).parser;
49055
+ const numberOrString = union(number).or(string).parser;
49056
+ const numeric = pipeParsers(defaulted(numberOrString, () => NaN)).then(stringToNumber).parser;
49099
49057
  const parseMatrixWidget = parseWidget(defaulted(constant("matrix"), () => "matrix"), object({
49100
49058
  prefix: optional(string),
49101
49059
  suffix: optional(string),
@@ -49114,7 +49072,8 @@ const parseMeasurerWidget = parseWidget(constant("measurer"), object({
49114
49072
  rulerPixels: number,
49115
49073
  rulerLength: number,
49116
49074
  box: pair(number, number),
49117
- static: boolean
49075
+ // TODO(benchristel): static is not used. Remove it?
49076
+ static: defaulted(boolean, () => false)
49118
49077
  }));
49119
49078
 
49120
49079
  const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), object({
@@ -49123,9 +49082,10 @@ const parseMoleculeRendererWidget = parseWidget(constant("molecule-renderer"), o
49123
49082
  smiles: optional(string)
49124
49083
  }));
49125
49084
 
49085
+ const emptyStringToNull = pipeParsers(constant("")).then(convert(() => null)).parser;
49126
49086
  const parseNumberLineWidget = parseWidget(constant("number-line"), object({
49127
49087
  range: array(number),
49128
- labelRange: array(nullable(number)),
49088
+ labelRange: array(nullable(union(number).or(emptyStringToNull).parser)),
49129
49089
  labelStyle: string,
49130
49090
  labelTicks: boolean,
49131
49091
  isTickCtrl: optional(nullable(boolean)),
@@ -49137,10 +49097,10 @@ const parseNumberLineWidget = parseWidget(constant("number-line"), object({
49137
49097
  snapDivisions: defaulted(number, () => 2),
49138
49098
  tickStep: optional(nullable(number)),
49139
49099
  correctRel: optional(nullable(string)),
49140
- correctX: number,
49100
+ correctX: nullable(number),
49141
49101
  initialX: optional(nullable(number)),
49142
49102
  showTooltip: optional(boolean),
49143
- static: boolean
49103
+ static: defaulted(boolean, () => false)
49144
49104
  }));
49145
49105
 
49146
49106
  const parseMathFormat = enumeration("integer", "mixed", "improper", "proper", "decimal", "percent", "pi");
@@ -49155,14 +49115,14 @@ const parseNumericInputWidget = parseWidget(constant("numeric-input"), object({
49155
49115
  answerForms: optional(array(parseMathFormat)),
49156
49116
  strict: boolean,
49157
49117
  maxError: optional(nullable(number)),
49158
- // TODO(benchristel): simplify should never be `true`, but we
49118
+ // TODO(benchristel): simplify should never be a boolean, but we
49159
49119
  // have some content where it is anyway. If we ever backfill
49160
49120
  // the data, we should simplify `simplify`.
49161
- simplify: optional(nullable(union(string).or(pipeParsers(constant(true)).then(convert(String)).parser).parser))
49121
+ simplify: optional(nullable(union(string).or(pipeParsers(boolean).then(convert(String)).parser).parser))
49162
49122
  })),
49163
49123
  labelText: optional(string),
49164
49124
  size: string,
49165
- coefficient: boolean,
49125
+ coefficient: defaulted(boolean, () => false),
49166
49126
  rightAlign: optional(boolean),
49167
49127
  static: defaulted(boolean, () => false),
49168
49128
  answerForms: optional(array(object({
@@ -49198,9 +49158,9 @@ const parsePassageRefWidget = parseWidget(constant("passage-ref"), object({
49198
49158
  }));
49199
49159
 
49200
49160
  const parsePassageWidget = parseWidget(constant("passage"), object({
49201
- footnotes: string,
49161
+ footnotes: defaulted(string, () => ""),
49202
49162
  passageText: string,
49203
- passageTitle: string,
49163
+ passageTitle: defaulted(string, () => ""),
49204
49164
  showLineNumbers: boolean,
49205
49165
  static: defaulted(boolean, () => false)
49206
49166
  }));
@@ -49385,6 +49345,10 @@ const parseWidgetsMapEntry = (_ref, widgetMap, ctx) => {
49385
49345
  return parseAndAssign(`sequence ${id}`, parseDeprecatedWidget);
49386
49346
  case "lights-puzzle":
49387
49347
  return parseAndAssign(`lights-puzzle ${id}`, parseDeprecatedWidget);
49348
+ case "simulator":
49349
+ return parseAndAssign(`simulator ${id}`, parseDeprecatedWidget);
49350
+ case "transformer":
49351
+ return parseAndAssign(`transformer ${id}`, parseDeprecatedWidget);
49388
49352
  default:
49389
49353
  if (getWidget(type)) {
49390
49354
  // @ts-expect-error - 'type' is not a valid widget type