@khanacademy/perseus-core 5.4.1 → 5.4.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
@@ -4,7 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var _ = require('underscore');
6
6
  var KAS = require('@khanacademy/kas');
7
- var perseusCore = require('@khanacademy/perseus-core');
7
+ var perseusUtils = require('@khanacademy/perseus-utils');
8
8
 
9
9
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
10
10
 
@@ -2780,10 +2780,10 @@ const parsePerseusItem$1 = object({
2780
2780
  question: parsePerseusRenderer,
2781
2781
  hints: defaulted(array(parseHint), () => []),
2782
2782
  answerArea: parsePerseusAnswerArea,
2783
- itemDataVersion: optional(object({
2783
+ itemDataVersion: optional(nullable(object({
2784
2784
  major: number,
2785
2785
  minor: number
2786
- })),
2786
+ }))),
2787
2787
  // Deprecated field
2788
2788
  answer: any
2789
2789
  });
@@ -2860,51 +2860,10 @@ function throwErrorIfCheatingDetected() {
2860
2860
  }
2861
2861
  }
2862
2862
 
2863
- /**
2864
- * Adds the given perseus library version information to the __perseus_debug__
2865
- * object and ensures that the object is attached to `globalThis` (`window` in
2866
- * browser environments).
2867
- *
2868
- * This allows each library to provide runtime version information to assist in
2869
- * debugging in production environments.
2870
- */
2871
- const addLibraryVersionToPerseusDebug = (libraryName, libraryVersion) => {
2872
- // If the library version is the default value, then we don't want to
2873
- // prefix it with a "v" to indicate that it is a version number.
2874
- let prefix = "v";
2875
- if (libraryVersion === "__lib_version__") {
2876
- prefix = "";
2877
- }
2878
- const formattedVersion = `${prefix}${libraryVersion}`;
2879
- if (typeof globalThis !== "undefined") {
2880
- globalThis.__perseus_debug__ = globalThis.__perseus_debug__ ?? {};
2881
- const existingVersionEntry = globalThis.__perseus_debug__[libraryName];
2882
- if (existingVersionEntry) {
2883
- // If we already have an entry and it doesn't match the registered
2884
- // version, we morph the entry into an array and log a warning.
2885
- if (existingVersionEntry !== formattedVersion) {
2886
- // Existing entry might be an array already (oops, at least 2
2887
- // versions of the library already loaded!).
2888
- const allVersions = Array.isArray(existingVersionEntry) ? existingVersionEntry : [existingVersionEntry];
2889
- allVersions.push(formattedVersion);
2890
- globalThis.__perseus_debug__[libraryName] = allVersions;
2891
-
2892
- // eslint-disable-next-line no-console
2893
- console.warn(`Multiple versions of ${libraryName} loaded on this page: ${allVersions.sort().join(", ")}`);
2894
- }
2895
- } else {
2896
- globalThis.__perseus_debug__[libraryName] = formattedVersion;
2897
- }
2898
- } else {
2899
- // eslint-disable-next-line no-console
2900
- console.warn(`globalThis not found found (${formattedVersion})`);
2901
- }
2902
- };
2903
-
2904
2863
  // This file is processed by a Rollup plugin (replace) to inject the production
2905
2864
  const libName = "@khanacademy/perseus-core";
2906
- const libVersion = "5.4.1";
2907
- addLibraryVersionToPerseusDebug(libName, libVersion);
2865
+ const libVersion = "5.4.2";
2866
+ perseusUtils.addLibraryVersionToPerseusDebug(libName, libVersion);
2908
2867
 
2909
2868
  /**
2910
2869
  * @typedef {Object} Errors utility for referencing the Perseus error taxonomy.
@@ -3387,20 +3346,74 @@ const labelImageWidgetLogic = {
3387
3346
  getPublicWidgetOptions: getLabelImagePublicWidgetOptions
3388
3347
  };
3389
3348
 
3349
+ /* Note(tamara): Brought over from the perseus package packages/perseus/src/util.ts file.
3350
+ May be useful to bring other perseus package utilities here. Contains utility functions
3351
+ and types used across multiple widgets for randomization and shuffling. */
3352
+ const seededRNG = function (seed) {
3353
+ let randomSeed = seed;
3354
+ return function () {
3355
+ // Robert Jenkins' 32 bit integer hash function.
3356
+ let seed = randomSeed;
3357
+ seed = seed + 0x7ed55d16 + (seed << 12) & 0xffffffff;
3358
+ seed = (seed ^ 0xc761c23c ^ seed >>> 19) & 0xffffffff;
3359
+ seed = seed + 0x165667b1 + (seed << 5) & 0xffffffff;
3360
+ seed = (seed + 0xd3a2646c ^ seed << 9) & 0xffffffff;
3361
+ seed = seed + 0xfd7046c5 + (seed << 3) & 0xffffffff;
3362
+ seed = (seed ^ 0xb55a4f09 ^ seed >>> 16) & 0xffffffff;
3363
+ return (randomSeed = seed & 0xfffffff) / 0x10000000;
3364
+ };
3365
+ };
3366
+
3367
+ // Shuffle an array using a given random seed or function.
3368
+ // If `ensurePermuted` is true, the input and output are guaranteed to be
3369
+ // distinct permutations.
3370
+ function shuffle(array, randomSeed) {
3371
+ let ensurePermuted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
3372
+ // Always return a copy of the input array
3373
+ const shuffled = ___default["default"].clone(array);
3374
+
3375
+ // Handle edge cases (input array is empty or uniform)
3376
+ if (!shuffled.length || ___default["default"].all(shuffled, function (value) {
3377
+ return ___default["default"].isEqual(value, shuffled[0]);
3378
+ })) {
3379
+ return shuffled;
3380
+ }
3381
+ let random;
3382
+ if (typeof randomSeed === "function") {
3383
+ random = randomSeed;
3384
+ } else {
3385
+ random = seededRNG(randomSeed);
3386
+ }
3387
+ do {
3388
+ // Fischer-Yates shuffle
3389
+ for (let top = shuffled.length; top > 0; top--) {
3390
+ const newEnd = Math.floor(random() * top);
3391
+ const temp = shuffled[newEnd];
3392
+
3393
+ // @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
3394
+ shuffled[newEnd] = shuffled[top - 1];
3395
+ // @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
3396
+ shuffled[top - 1] = temp;
3397
+ }
3398
+ } while (ensurePermuted && ___default["default"].isEqual(array, shuffled));
3399
+ return shuffled;
3400
+ }
3401
+ const random = seededRNG(new Date().getTime() & 0xffffffff);
3402
+
3390
3403
  // TODO(LEMS-2841): Should be able to remove once getPublicWidgetOptions is hooked up
3391
3404
 
3392
3405
  // TODO(LEMS-2841): Should be able to remove once getPublicWidgetOptions is hooked up
3393
3406
  const shuffleMatcher = props => {
3394
3407
  // Use the same random() function to shuffle both columns sequentially
3395
- const rng = perseusCore.seededRNG(props.problemNum);
3408
+ const rng = seededRNG(props.problemNum);
3396
3409
  let left;
3397
3410
  if (!props.orderMatters) {
3398
3411
  // If the order doesn't matter, don't shuffle the left column
3399
3412
  left = props.left;
3400
3413
  } else {
3401
- left = perseusCore.shuffle(props.left, rng, /* ensurePermuted */true);
3414
+ left = shuffle(props.left, rng, /* ensurePermuted */true);
3402
3415
  }
3403
- const right = perseusCore.shuffle(props.right, rng, /* ensurePermuted */true);
3416
+ const right = shuffle(props.right, rng, /* ensurePermuted */true);
3404
3417
  return {
3405
3418
  left,
3406
3419
  right
@@ -3415,9 +3428,9 @@ function shuffleMatcherWithRandom(data) {
3415
3428
  // If the order doesn't matter, don't shuffle the left column
3416
3429
  left = data.left;
3417
3430
  } else {
3418
- left = perseusCore.shuffle(data.left, Math.random, /* ensurePermuted */true);
3431
+ left = shuffle(data.left, Math.random, /* ensurePermuted */true);
3419
3432
  }
3420
- const right = perseusCore.shuffle(data.right, Math.random, /* ensurePermuted */true);
3433
+ const right = shuffle(data.right, Math.random, /* ensurePermuted */true);
3421
3434
  return {
3422
3435
  left,
3423
3436
  right
@@ -3807,7 +3820,7 @@ const radioWidgetLogic = {
3807
3820
  * the public options that should be exposed to the client.
3808
3821
  */
3809
3822
  function getSorterPublicWidgetOptions(options) {
3810
- const shuffledCorrect = perseusCore.shuffle(options.correct, Math.random, /* ensurePermuted */true);
3823
+ const shuffledCorrect = shuffle(options.correct, Math.random, /* ensurePermuted */true);
3811
3824
  return {
3812
3825
  ...options,
3813
3826
  // Note(Tamara): This does not provide correct answer information any longer.
@@ -4129,60 +4142,6 @@ function splitPerseusItem(originalItem) {
4129
4142
  };
4130
4143
  }
4131
4144
 
4132
- /* Note(tamara): Brought over from the perseus package packages/perseus/src/util.ts file.
4133
- May be useful to bring other perseus package utilities here. Contains utility functions
4134
- and types used across multiple widgets for randomization and shuffling. */
4135
- const seededRNG = function (seed) {
4136
- let randomSeed = seed;
4137
- return function () {
4138
- // Robert Jenkins' 32 bit integer hash function.
4139
- let seed = randomSeed;
4140
- seed = seed + 0x7ed55d16 + (seed << 12) & 0xffffffff;
4141
- seed = (seed ^ 0xc761c23c ^ seed >>> 19) & 0xffffffff;
4142
- seed = seed + 0x165667b1 + (seed << 5) & 0xffffffff;
4143
- seed = (seed + 0xd3a2646c ^ seed << 9) & 0xffffffff;
4144
- seed = seed + 0xfd7046c5 + (seed << 3) & 0xffffffff;
4145
- seed = (seed ^ 0xb55a4f09 ^ seed >>> 16) & 0xffffffff;
4146
- return (randomSeed = seed & 0xfffffff) / 0x10000000;
4147
- };
4148
- };
4149
-
4150
- // Shuffle an array using a given random seed or function.
4151
- // If `ensurePermuted` is true, the input and output are guaranteed to be
4152
- // distinct permutations.
4153
- function shuffle(array, randomSeed) {
4154
- let ensurePermuted = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
4155
- // Always return a copy of the input array
4156
- const shuffled = ___default["default"].clone(array);
4157
-
4158
- // Handle edge cases (input array is empty or uniform)
4159
- if (!shuffled.length || ___default["default"].all(shuffled, function (value) {
4160
- return ___default["default"].isEqual(value, shuffled[0]);
4161
- })) {
4162
- return shuffled;
4163
- }
4164
- let random;
4165
- if (typeof randomSeed === "function") {
4166
- random = randomSeed;
4167
- } else {
4168
- random = seededRNG(randomSeed);
4169
- }
4170
- do {
4171
- // Fischer-Yates shuffle
4172
- for (let top = shuffled.length; top > 0; top--) {
4173
- const newEnd = Math.floor(random() * top);
4174
- const temp = shuffled[newEnd];
4175
-
4176
- // @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
4177
- shuffled[newEnd] = shuffled[top - 1];
4178
- // @ts-expect-error - TS2542 - Index signature in type 'readonly T[]' only permits reading.
4179
- shuffled[top - 1] = temp;
4180
- }
4181
- } while (ensurePermuted && ___default["default"].isEqual(array, shuffled));
4182
- return shuffled;
4183
- }
4184
- const random = seededRNG(new Date().getTime() & 0xffffffff);
4185
-
4186
4145
  exports.CoreWidgetRegistry = coreWidgetRegistry;
4187
4146
  exports.Errors = Errors;
4188
4147
  exports.GrapherUtil = grapherUtil;