playcademy 0.31.1-beta.2 → 0.31.1-beta.4

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/bucket.js CHANGED
@@ -42,7 +42,7 @@ var GAME_API_ROUTES_DIRECTORY = `${GAME_SERVER_ROOT_DIRECTORY}/api`;
42
42
  // ../better-auth/package.json
43
43
  var package_default = {
44
44
  name: "@playcademy/better-auth",
45
- version: "0.0.24-beta.2",
45
+ version: "0.0.24-beta.4",
46
46
  type: "module",
47
47
  exports: {
48
48
  "./server": {
package/dist/cli.js CHANGED
@@ -1208,7 +1208,7 @@ var SAMPLE_BUCKET_FILENAME = "bucket.ts";
1208
1208
  // ../better-auth/package.json
1209
1209
  var package_default = {
1210
1210
  name: "@playcademy/better-auth",
1211
- version: "0.0.24-beta.2",
1211
+ version: "0.0.24-beta.4",
1212
1212
  type: "module",
1213
1213
  exports: {
1214
1214
  "./server": {
@@ -3045,7 +3045,7 @@ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as re
3045
3045
  import { join as join12 } from "node:path";
3046
3046
 
3047
3047
  // src/version.ts
3048
- var cliVersion = false ? "0.0.0-dev" : "0.31.1-beta.2";
3048
+ var cliVersion = false ? "0.0.0-dev" : "0.31.1-beta.4";
3049
3049
 
3050
3050
  // src/lib/init/database.ts
3051
3051
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
package/dist/constants.js CHANGED
@@ -21,7 +21,7 @@ var SAMPLE_BUCKET_FILENAME = "bucket.ts";
21
21
  // ../better-auth/package.json
22
22
  var package_default = {
23
23
  name: "@playcademy/better-auth",
24
- version: "0.0.24-beta.2",
24
+ version: "0.0.24-beta.4",
25
25
  type: "module",
26
26
  exports: {
27
27
  "./server": {
package/dist/db.js CHANGED
@@ -2333,7 +2333,7 @@ var GAME_API_ROUTES_DIRECTORY = `${GAME_SERVER_ROOT_DIRECTORY}/api`;
2333
2333
  // ../better-auth/package.json
2334
2334
  var package_default = {
2335
2335
  name: "@playcademy/better-auth",
2336
- version: "0.0.24-beta.2",
2336
+ version: "0.0.24-beta.4",
2337
2337
  type: "module",
2338
2338
  exports: {
2339
2339
  "./server": {
package/dist/index.js CHANGED
@@ -2345,7 +2345,7 @@ var SAMPLE_BUCKET_FILENAME = "bucket.ts";
2345
2345
  // ../better-auth/package.json
2346
2346
  var package_default = {
2347
2347
  name: "@playcademy/better-auth",
2348
- version: "0.0.24-beta.2",
2348
+ version: "0.0.24-beta.4",
2349
2349
  type: "module",
2350
2350
  exports: {
2351
2351
  "./server": {
@@ -2636,6 +2636,7 @@ var TIMEBACK_SUBJECTS = [
2636
2636
  "None"
2637
2637
  ];
2638
2638
  var ASSESSMENT_PURPOSES = ["end_of_course", "diagnostic", "review", "mastery"];
2639
+ var PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION = 2;
2639
2640
  var TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS = {
2640
2641
  standards: 20,
2641
2642
  candidateItemsPerStandard: 2,
@@ -6486,7 +6487,7 @@ import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as re
6486
6487
  import { join as join12 } from "node:path";
6487
6488
 
6488
6489
  // src/version.ts
6489
- var cliVersion = false ? "0.0.0-dev" : "0.31.1-beta.2";
6490
+ var cliVersion = false ? "0.0.0-dev" : "0.31.1-beta.4";
6490
6491
 
6491
6492
  // src/lib/init/database.ts
6492
6493
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
@@ -22746,6 +22747,23 @@ function isFixtureDiagnosticRouting(value, assessment, correctResponses, respons
22746
22747
  return validation.valid;
22747
22748
  }
22748
22749
  var ASSESSMENT_RUNTIME_FIXTURE_BUNDLE_VERSION = 8;
22750
+ function hasValidFixtureShuffle(interaction) {
22751
+ if (!isRecord2(interaction)) {
22752
+ return false;
22753
+ }
22754
+ switch (interaction.type) {
22755
+ case "choice":
22756
+ case "inline-choice":
22757
+ case "order":
22758
+ case "match":
22759
+ case "gap-match": {
22760
+ return typeof interaction.shuffle === "boolean";
22761
+ }
22762
+ default: {
22763
+ return true;
22764
+ }
22765
+ }
22766
+ }
22749
22767
  function isAssessmentRuntimeFixtureBundle(value) {
22750
22768
  if (!isRecord2(value) || value.version !== ASSESSMENT_RUNTIME_FIXTURE_BUNDLE_VERSION || typeof value.gameId !== "string" || typeof value.exportedAt !== "string" || !Array.isArray(value.catalogs)) {
22751
22769
  return false;
@@ -22758,7 +22776,7 @@ function isAssessmentRuntimeFixtureBundle(value) {
22758
22776
  return false;
22759
22777
  }
22760
22778
  const validTests = catalog.tests.every((test) => {
22761
- if (!isRecord2(test) || typeof test.id !== "string" || typeof test.assessmentKey !== "string" || typeof test.qtiTestIdentifier !== "string" || typeof test.live !== "boolean" || !isRecord2(test.assessment) || typeof test.assessment.identifier !== "string" || !Number.isInteger(test.assessment.contractVersion) || typeof test.assessment.contentRevision !== "string" || !Array.isArray(test.assessment.items) || !test.assessment.items.every((item) => isRecord2(item) && typeof item.maxScore === "number" && Number.isFinite(item.maxScore) && item.maxScore > 0) || containsPrivatePlayableAssessmentData(test.assessment) || !isRecord2(test.correctResponses)) {
22779
+ if (!isRecord2(test) || typeof test.id !== "string" || typeof test.assessmentKey !== "string" || typeof test.qtiTestIdentifier !== "string" || typeof test.live !== "boolean" || !isRecord2(test.assessment) || typeof test.assessment.identifier !== "string" || test.assessment.contractVersion !== PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION || typeof test.assessment.contentRevision !== "string" || !Array.isArray(test.assessment.items) || !test.assessment.items.every((item) => isRecord2(item) && typeof item.maxScore === "number" && Number.isFinite(item.maxScore) && item.maxScore > 0 && Array.isArray(item.interactions) && item.interactions.every(hasValidFixtureShuffle)) || containsPrivatePlayableAssessmentData(test.assessment) || !isRecord2(test.correctResponses)) {
22762
22780
  return false;
22763
22781
  }
22764
22782
  const standard = isRecord2(test.standard) && typeof test.standard.framework === "string" && typeof test.standard.identifier === "string" ? canonicalReviewStandardRef({
@@ -22993,8 +23011,8 @@ var StartAssessmentBodySchema = external_exports.discriminatedUnion("purpose", [
22993
23011
  }),
22994
23012
  StartAssessmentBaseSchema.extend({
22995
23013
  purpose: external_exports.literal("review"),
22996
- requireBothQuestions: external_exports.boolean().optional(),
22997
- standards: external_exports.array(AssessmentStandardRefSchema2).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
23014
+ requireBothQuestions: external_exports.never().optional(),
23015
+ standards: external_exports.array(AssessmentStandardRefSchema2.and(external_exports.object({ requireBothQuestions: external_exports.boolean().optional() }))).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
22998
23016
  candidateItemsPerStandard: external_exports.number().int().positive().max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard).optional()
22999
23017
  }),
23000
23018
  StartAssessmentBaseSchema.extend({
@@ -1,7 +1,7 @@
1
1
  {
2
- "cliVersion": "0.31.1-beta.2",
3
- "sdkVersion": "0.19.1-beta.3",
4
- "runtimeBuildId": "4673549da4d8",
5
- "inputFingerprint": "4673549da4d81b760c8c8270835a45d570cb44eef97ca9052ba75da4876260e5",
2
+ "cliVersion": "0.31.1-beta.4",
3
+ "sdkVersion": "0.19.1-beta.5",
4
+ "runtimeBuildId": "343ae820e8a8",
5
+ "inputFingerprint": "343ae820e8a8c54a24dedf1f3e8c63919ec1ac915242aad290233f6b0b01d2f4",
6
6
  "entry": "index.js"
7
7
  }
@@ -94,7 +94,7 @@ var init_system = __esm({
94
94
  });
95
95
 
96
96
  // ../constants/src/timeback.ts
97
- var TIMEBACK_ROUTES, TIMEBACK_GRADES, TIMEBACK_SUBJECTS, ASSESSMENT_PURPOSES, TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS, TIMEBACK_ASSESSMENT_RESPONSE_VALUE_MAX_LENGTH, TIMEBACK_GAME_METRIC_DECIMAL_PLACES, TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE;
97
+ var TIMEBACK_ROUTES, TIMEBACK_GRADES, TIMEBACK_SUBJECTS, ASSESSMENT_PURPOSES, PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION, TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS, TIMEBACK_ASSESSMENT_RESPONSE_VALUE_MAX_LENGTH, TIMEBACK_GAME_METRIC_DECIMAL_PLACES, TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE;
98
98
  var init_timeback = __esm({
99
99
  "../constants/src/timeback.ts"() {
100
100
  "use strict";
@@ -121,6 +121,7 @@ var init_timeback = __esm({
121
121
  "None"
122
122
  ];
123
123
  ASSESSMENT_PURPOSES = ["end_of_course", "diagnostic", "review", "mastery"];
124
+ PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION = 2;
124
125
  TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS = {
125
126
  standards: 20,
126
127
  candidateItemsPerStandard: 2,
@@ -11466,30 +11467,60 @@ function comparePresentationText(left, right) {
11466
11467
  }
11467
11468
  return 0;
11468
11469
  }
11470
+ function presentationRandomIndex(seed) {
11471
+ let state = presentationHash(seed);
11472
+ const range = 2 ** 32;
11473
+ return (bound) => {
11474
+ const limit = range - range % bound;
11475
+ let value;
11476
+ do {
11477
+ state = state + 1831565813 >>> 0;
11478
+ let mixed = Math.imul(state ^ state >>> 15, state | 1);
11479
+ mixed ^= mixed + Math.imul(mixed ^ mixed >>> 7, mixed | 61);
11480
+ value = (mixed ^ mixed >>> 14) >>> 0;
11481
+ } while (value >= limit);
11482
+ return value % bound;
11483
+ };
11484
+ }
11469
11485
  function presentationChoices(choices, attemptId, itemIdentifier, domain) {
11470
- const seed = `${attemptId}\0${itemIdentifier}\0${domain}\0`;
11471
- return [...choices].toSorted((left, right) => {
11472
- const hashDifference = presentationHash(`${seed}${left.identifier}`) - presentationHash(`${seed}${right.identifier}`);
11473
- return hashDifference || comparePresentationText(left.identifier, right.identifier);
11474
- });
11486
+ const randomIndex = presentationRandomIndex(JSON.stringify([attemptId, itemIdentifier, domain]));
11487
+ const movable = choices.filter((choice) => !choice.fixed).toSorted((left, right) => comparePresentationText(left.identifier, right.identifier));
11488
+ for (let index = movable.length - 1; index > 0; index--) {
11489
+ const other = randomIndex(index + 1);
11490
+ [movable[index], movable[other]] = [movable[other], movable[index]];
11491
+ }
11492
+ let next = 0;
11493
+ return choices.map((choice) => choice.fixed ? choice : movable[next++]);
11475
11494
  }
11476
11495
  function assessmentPresentationForAttempt(assessment, attemptId) {
11477
11496
  const items = assessment.items.map((item) => {
11478
11497
  const interactions = item.interactions.map((interaction) => {
11479
- if (interaction.type === "order" && interaction.shuffle) {
11480
- return {
11481
- ...interaction,
11482
- choices: presentationChoices(interaction.choices, attemptId, item.identifier, `order\0${interaction.responseIdentifier}`)
11483
- };
11498
+ if (!("shuffle" in interaction) || !interaction.shuffle) {
11499
+ return interaction;
11484
11500
  }
11485
- if (interaction.type === "match" && interaction.shuffle) {
11486
- return {
11487
- ...interaction,
11488
- sourceChoices: presentationChoices(interaction.sourceChoices, attemptId, item.identifier, `match-source\0${interaction.responseIdentifier}`),
11489
- targetChoices: presentationChoices(interaction.targetChoices, attemptId, item.identifier, `match-target\0${interaction.responseIdentifier}`)
11490
- };
11501
+ function shuffled(choices, domain) {
11502
+ return presentationChoices(choices, attemptId, item.identifier, `${domain}\0${interaction.responseIdentifier}`);
11503
+ }
11504
+ switch (interaction.type) {
11505
+ case "choice":
11506
+ case "inline-choice":
11507
+ case "order": {
11508
+ return {
11509
+ ...interaction,
11510
+ choices: shuffled(interaction.choices, interaction.type)
11511
+ };
11512
+ }
11513
+ case "gap-match": {
11514
+ return { ...interaction, gapTexts: shuffled(interaction.gapTexts, "gap-match") };
11515
+ }
11516
+ case "match": {
11517
+ return {
11518
+ ...interaction,
11519
+ sourceChoices: shuffled(interaction.sourceChoices, "match-source"),
11520
+ targetChoices: shuffled(interaction.targetChoices, "match-target")
11521
+ };
11522
+ }
11491
11523
  }
11492
- return interaction;
11493
11524
  });
11494
11525
  return { ...item, interactions };
11495
11526
  });
@@ -11634,19 +11665,22 @@ function scorePlatformAssessmentItem(item, responses) {
11634
11665
  isCorrect: verdicts.length > 0 && verdicts.every(Boolean)
11635
11666
  };
11636
11667
  }
11668
+ function reviewStandardRef({
11669
+ framework,
11670
+ identifier
11671
+ }) {
11672
+ return { framework, identifier };
11673
+ }
11637
11674
  function reportReviewRequiredPairUnavailable(review, administeredItemIdentifiers2, report) {
11638
- if (!review.requireBothQuestions) {
11639
- return;
11640
- }
11641
11675
  const administered = new Set(administeredItemIdentifiers2);
11642
11676
  const failures = [];
11643
- for (const standard of review.standards) {
11677
+ for (const standard of review.standards.filter((entry) => entry.requireBothQuestions)) {
11644
11678
  const key = assessmentStandardRefKey(standard);
11645
11679
  const selected = new Set(review.selections.filter((selection) => assessmentStandardRefKey(selection.standard) === key).map((selection) => selection.itemIdentifier));
11646
11680
  const administeredCount = [...selected].filter((identifier) => administered.has(identifier)).length;
11647
11681
  if (selected.size < 2 || administeredCount === 1) {
11648
11682
  failures.push({
11649
- standard,
11683
+ standard: reviewStandardRef(standard),
11650
11684
  reason: selected.size < 2 ? "selection_shortage" : "not_administered",
11651
11685
  requiredCount: 2,
11652
11686
  selectedCount: selected.size,
@@ -11677,6 +11711,9 @@ function canonicalRequestStandards(standards) {
11677
11711
  }
11678
11712
  const canonical = /* @__PURE__ */ new Map();
11679
11713
  for (const input of standards) {
11714
+ if (input.requireBothQuestions !== void 0 && typeof input.requireBothQuestions !== "boolean") {
11715
+ throw new Error("review standard requireBothQuestions must be a boolean");
11716
+ }
11680
11717
  const standard = canonicalAssessmentStandardRef(input);
11681
11718
  if (!reviewStandardFieldsWithinLimits(standard)) {
11682
11719
  throw new RangeError(`Review standard fields may contain at most ${TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standardFieldLength} characters`);
@@ -11685,13 +11722,19 @@ function canonicalRequestStandards(standards) {
11685
11722
  throw new Error("Review standards require a framework and identifier");
11686
11723
  }
11687
11724
  const key = assessmentStandardRefKey(standard);
11688
- if (!canonical.has(key)) {
11689
- canonical.set(key, standard);
11725
+ if (!canonical.has(key) || input.requireBothQuestions) {
11726
+ canonical.set(key, {
11727
+ ...standard,
11728
+ ...input.requireBothQuestions ? { requireBothQuestions: true } : {}
11729
+ });
11690
11730
  }
11691
11731
  }
11692
- return [...canonical.values()].toSorted((left, right) => compareCodeUnits(assessmentStandardRefKey(left), assessmentStandardRefKey(right)));
11732
+ return [...canonical.values()];
11693
11733
  }
11694
11734
  function normalizeReviewRequest(request2) {
11735
+ if ("requireBothQuestions" in request2 && request2.requireBothQuestions !== void 0) {
11736
+ throw new Error("Set requireBothQuestions on individual review standards, not the review request");
11737
+ }
11695
11738
  const candidateItemsPerStandard = request2.candidateItemsPerStandard ?? 1;
11696
11739
  if (!Number.isInteger(candidateItemsPerStandard) || candidateItemsPerStandard <= 0 || candidateItemsPerStandard > TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard) {
11697
11740
  throw new RangeError(`candidateItemsPerStandard must be a positive integer at most ${TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard}`);
@@ -11702,20 +11745,20 @@ function normalizeReviewRequest(request2) {
11702
11745
  }
11703
11746
  return {
11704
11747
  standards,
11705
- candidateItemsPerStandard,
11706
- ...request2.requireBothQuestions ? { requireBothQuestions: true } : {}
11748
+ candidateItemsPerStandard
11707
11749
  };
11708
11750
  }
11709
11751
  function reviewRequestFingerprint(request2, policy) {
11710
11752
  const normalized = normalizeReviewRequest(request2);
11753
+ const requiredStandardKeys = normalized.standards.filter((standard) => standard.requireBothQuestions).map(assessmentStandardRefKey);
11711
11754
  return JSON.stringify({
11712
11755
  kind: "standards-review",
11713
11756
  policy: {
11714
11757
  version: policy.version
11715
11758
  },
11716
11759
  candidateItemsPerStandard: normalized.candidateItemsPerStandard,
11717
- ...normalized.requireBothQuestions ? { requireBothQuestions: true } : {},
11718
- standardKeys: normalized.standards.map(assessmentStandardRefKey)
11760
+ standardKeys: normalized.standards.map(assessmentStandardRefKey),
11761
+ ...requiredStandardKeys.length > 0 ? { requiredStandardKeys } : {}
11719
11762
  });
11720
11763
  }
11721
11764
  function exposureTimestamp(value) {
@@ -11765,9 +11808,10 @@ function rankedCandidatesByStandard(bank, exposures) {
11765
11808
  return byStandard;
11766
11809
  }
11767
11810
  function reviewSlots(request2) {
11768
- return request2.standards.flatMap((standard) => Array.from({ length: request2.candidateItemsPerStandard }, () => ({
11769
- standard,
11770
- standardKey: assessmentStandardRefKey(standard)
11811
+ return request2.standards.flatMap((standard, servingIndex) => Array.from({ length: request2.candidateItemsPerStandard }, () => ({
11812
+ standard: reviewStandardRef(standard),
11813
+ standardKey: assessmentStandardRefKey(standard),
11814
+ servingIndex
11771
11815
  })));
11772
11816
  }
11773
11817
  function matchingCandidates(candidatesByStandard, slotCount) {
@@ -11880,7 +11924,7 @@ function fulfillment(request2, candidatesByStandard, selections) {
11880
11924
  const selected = selectedByStandard.get(key) ?? 0;
11881
11925
  return selected < request2.candidateItemsPerStandard ? [
11882
11926
  {
11883
- standard,
11927
+ standard: reviewStandardRef(standard),
11884
11928
  requested: request2.candidateItemsPerStandard,
11885
11929
  selected,
11886
11930
  eligible: candidatesByStandard.get(key)?.length ?? 0
@@ -11905,10 +11949,10 @@ function fulfillment(request2, candidatesByStandard, selections) {
11905
11949
  function selectReviewItems(input) {
11906
11950
  const request2 = normalizeReviewRequest(input.request);
11907
11951
  const candidatesByStandard = rankedCandidatesByStandard(input.bank, input.exposures);
11908
- const slots = reviewSlots(request2);
11952
+ const slots = reviewSlots(request2).toSorted((left, right) => compareCodeUnits(left.standardKey, right.standardKey));
11909
11953
  const boundedCandidates = matchingCandidates(candidatesByStandard, slots.length);
11910
11954
  const selected = preferredDistinctMatching(slots, boundedCandidates);
11911
- const selections = [...selected.entries()].toSorted(([left], [right]) => left - right).map(([index, itemIdentifier]) => ({
11955
+ const selections = [...selected.entries()].toSorted(([left], [right]) => slots[left].servingIndex - slots[right].servingIndex || left - right).map(([index, itemIdentifier]) => ({
11912
11956
  standard: slots[index].standard,
11913
11957
  itemIdentifier
11914
11958
  }));
@@ -12124,6 +12168,23 @@ function isFixtureDiagnosticRouting(value, assessment, correctResponses, respons
12124
12168
  });
12125
12169
  return validation.valid;
12126
12170
  }
12171
+ function hasValidFixtureShuffle(interaction) {
12172
+ if (!isRecord(interaction)) {
12173
+ return false;
12174
+ }
12175
+ switch (interaction.type) {
12176
+ case "choice":
12177
+ case "inline-choice":
12178
+ case "order":
12179
+ case "match":
12180
+ case "gap-match": {
12181
+ return typeof interaction.shuffle === "boolean";
12182
+ }
12183
+ default: {
12184
+ return true;
12185
+ }
12186
+ }
12187
+ }
12127
12188
  function isAssessmentRuntimeFixtureBundle(value) {
12128
12189
  if (!isRecord(value) || value.version !== ASSESSMENT_RUNTIME_FIXTURE_BUNDLE_VERSION || typeof value.gameId !== "string" || typeof value.exportedAt !== "string" || !Array.isArray(value.catalogs)) {
12129
12190
  return false;
@@ -12136,7 +12197,7 @@ function isAssessmentRuntimeFixtureBundle(value) {
12136
12197
  return false;
12137
12198
  }
12138
12199
  const validTests = catalog.tests.every((test) => {
12139
- if (!isRecord(test) || typeof test.id !== "string" || typeof test.assessmentKey !== "string" || typeof test.qtiTestIdentifier !== "string" || typeof test.live !== "boolean" || !isRecord(test.assessment) || typeof test.assessment.identifier !== "string" || !Number.isInteger(test.assessment.contractVersion) || typeof test.assessment.contentRevision !== "string" || !Array.isArray(test.assessment.items) || !test.assessment.items.every((item) => isRecord(item) && typeof item.maxScore === "number" && Number.isFinite(item.maxScore) && item.maxScore > 0) || containsPrivatePlayableAssessmentData(test.assessment) || !isRecord(test.correctResponses)) {
12200
+ if (!isRecord(test) || typeof test.id !== "string" || typeof test.assessmentKey !== "string" || typeof test.qtiTestIdentifier !== "string" || typeof test.live !== "boolean" || !isRecord(test.assessment) || typeof test.assessment.identifier !== "string" || test.assessment.contractVersion !== PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION || typeof test.assessment.contentRevision !== "string" || !Array.isArray(test.assessment.items) || !test.assessment.items.every((item) => isRecord(item) && typeof item.maxScore === "number" && Number.isFinite(item.maxScore) && item.maxScore > 0 && Array.isArray(item.interactions) && item.interactions.every(hasValidFixtureShuffle)) || containsPrivatePlayableAssessmentData(test.assessment) || !isRecord(test.correctResponses)) {
12140
12201
  return false;
12141
12202
  }
12142
12203
  const standard = isRecord(test.standard) && typeof test.standard.framework === "string" && typeof test.standard.identifier === "string" ? canonicalReviewStandardRef({
@@ -12215,6 +12276,7 @@ var init_assessment_runtime2 = __esm({
12215
12276
  init_timeback4();
12216
12277
  init_src();
12217
12278
  init_timeback3();
12279
+ init_src();
12218
12280
  init_fns();
12219
12281
  init_src();
12220
12282
  init_fns();
@@ -12703,8 +12765,8 @@ var init_assessment_runtime2 = __esm({
12703
12765
  }),
12704
12766
  StartAssessmentBaseSchema.extend({
12705
12767
  purpose: external_exports.literal("review"),
12706
- requireBothQuestions: external_exports.boolean().optional(),
12707
- standards: external_exports.array(AssessmentStandardRefSchema2).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
12768
+ requireBothQuestions: external_exports.never().optional(),
12769
+ standards: external_exports.array(AssessmentStandardRefSchema2.and(external_exports.object({ requireBothQuestions: external_exports.boolean().optional() }))).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
12708
12770
  candidateItemsPerStandard: external_exports.number().int().positive().max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard).optional()
12709
12771
  }),
12710
12772
  StartAssessmentBaseSchema.extend({
@@ -13005,7 +13067,6 @@ var init_local_assessment_runtime = __esm({
13005
13067
  purpose: "review",
13006
13068
  standards: [...request2.standards],
13007
13069
  candidateItemsPerStandard: request2.candidateItemsPerStandard,
13008
- ...request2.requireBothQuestions ? { requireBothQuestions: true } : {},
13009
13070
  selections: [...selected.selections],
13010
13071
  fulfillment: selected.fulfillment
13011
13072
  },
@@ -14253,9 +14314,9 @@ var init_list = __esm({
14253
14314
  // ../edge-play/src/game/lib/metadata.ts
14254
14315
  function getRuntimeMetadata() {
14255
14316
  return {
14256
- cliVersion: true ? "0.31.1-beta.2" : "0.0.0-dev",
14257
- sdkVersion: true ? "0.19.1-beta.3" : "0.0.0-dev",
14258
- buildId: true ? "4673549da4d8" : "dev-source"
14317
+ cliVersion: true ? "0.31.1-beta.4" : "0.0.0-dev",
14318
+ sdkVersion: true ? "0.19.1-beta.5" : "0.0.0-dev",
14319
+ buildId: true ? "343ae820e8a8" : "dev-source"
14259
14320
  };
14260
14321
  }
14261
14322
  var init_metadata = __esm({
@@ -1,7 +1,7 @@
1
1
  {
2
- "cliVersion": "0.31.1-beta.2",
3
- "sdkVersion": "0.19.1-beta.3",
4
- "runtimeBuildId": "4673549da4d8",
5
- "inputFingerprint": "4673549da4d81b760c8c8270835a45d570cb44eef97ca9052ba75da4876260e5",
2
+ "cliVersion": "0.31.1-beta.4",
3
+ "sdkVersion": "0.19.1-beta.5",
4
+ "runtimeBuildId": "343ae820e8a8",
5
+ "inputFingerprint": "343ae820e8a8c54a24dedf1f3e8c63919ec1ac915242aad290233f6b0b01d2f4",
6
6
  "entry": "index.js"
7
7
  }
package/dist/utils.js CHANGED
@@ -342,6 +342,7 @@ var TIMEBACK_SUBJECTS = [
342
342
  "None"
343
343
  ];
344
344
  var ASSESSMENT_PURPOSES = ["end_of_course", "diagnostic", "review", "mastery"];
345
+ var PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION = 2;
345
346
  var TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS = {
346
347
  standards: 20,
347
348
  candidateItemsPerStandard: 2,
@@ -396,7 +397,7 @@ var DASHBOARD_API_ROUTES_DIRECTORY = "api";
396
397
  // ../better-auth/package.json
397
398
  var package_default = {
398
399
  name: "@playcademy/better-auth",
399
- version: "0.0.24-beta.2",
400
+ version: "0.0.24-beta.4",
400
401
  type: "module",
401
402
  exports: {
402
403
  "./server": {
@@ -2849,7 +2850,7 @@ import { existsSync as existsSync9, mkdirSync as mkdirSync2, writeFileSync as wr
2849
2850
  import { dirname as dirname4, join as join13 } from "node:path";
2850
2851
 
2851
2852
  // src/version.ts
2852
- var cliVersion = false ? "0.0.0-dev" : "0.31.1-beta.2";
2853
+ var cliVersion = false ? "0.0.0-dev" : "0.31.1-beta.4";
2853
2854
 
2854
2855
  // src/lib/build/binary-resource.ts
2855
2856
  function writeFileTree(baseDir, files) {
@@ -15514,6 +15515,23 @@ function isFixtureDiagnosticRouting(value, assessment, correctResponses, respons
15514
15515
  return validation.valid;
15515
15516
  }
15516
15517
  var ASSESSMENT_RUNTIME_FIXTURE_BUNDLE_VERSION = 8;
15518
+ function hasValidFixtureShuffle(interaction) {
15519
+ if (!isRecord(interaction)) {
15520
+ return false;
15521
+ }
15522
+ switch (interaction.type) {
15523
+ case "choice":
15524
+ case "inline-choice":
15525
+ case "order":
15526
+ case "match":
15527
+ case "gap-match": {
15528
+ return typeof interaction.shuffle === "boolean";
15529
+ }
15530
+ default: {
15531
+ return true;
15532
+ }
15533
+ }
15534
+ }
15517
15535
  function isAssessmentRuntimeFixtureBundle(value) {
15518
15536
  if (!isRecord(value) || value.version !== ASSESSMENT_RUNTIME_FIXTURE_BUNDLE_VERSION || typeof value.gameId !== "string" || typeof value.exportedAt !== "string" || !Array.isArray(value.catalogs)) {
15519
15537
  return false;
@@ -15526,7 +15544,7 @@ function isAssessmentRuntimeFixtureBundle(value) {
15526
15544
  return false;
15527
15545
  }
15528
15546
  const validTests = catalog.tests.every((test) => {
15529
- if (!isRecord(test) || typeof test.id !== "string" || typeof test.assessmentKey !== "string" || typeof test.qtiTestIdentifier !== "string" || typeof test.live !== "boolean" || !isRecord(test.assessment) || typeof test.assessment.identifier !== "string" || !Number.isInteger(test.assessment.contractVersion) || typeof test.assessment.contentRevision !== "string" || !Array.isArray(test.assessment.items) || !test.assessment.items.every((item) => isRecord(item) && typeof item.maxScore === "number" && Number.isFinite(item.maxScore) && item.maxScore > 0) || containsPrivatePlayableAssessmentData(test.assessment) || !isRecord(test.correctResponses)) {
15547
+ if (!isRecord(test) || typeof test.id !== "string" || typeof test.assessmentKey !== "string" || typeof test.qtiTestIdentifier !== "string" || typeof test.live !== "boolean" || !isRecord(test.assessment) || typeof test.assessment.identifier !== "string" || test.assessment.contractVersion !== PLAYCADEMY_ASSESSMENT_CONTRACT_VERSION || typeof test.assessment.contentRevision !== "string" || !Array.isArray(test.assessment.items) || !test.assessment.items.every((item) => isRecord(item) && typeof item.maxScore === "number" && Number.isFinite(item.maxScore) && item.maxScore > 0 && Array.isArray(item.interactions) && item.interactions.every(hasValidFixtureShuffle)) || containsPrivatePlayableAssessmentData(test.assessment) || !isRecord(test.correctResponses)) {
15530
15548
  return false;
15531
15549
  }
15532
15550
  const standard = isRecord(test.standard) && typeof test.standard.framework === "string" && typeof test.standard.identifier === "string" ? canonicalReviewStandardRef({
@@ -15761,8 +15779,8 @@ var StartAssessmentBodySchema = external_exports.discriminatedUnion("purpose", [
15761
15779
  }),
15762
15780
  StartAssessmentBaseSchema.extend({
15763
15781
  purpose: external_exports.literal("review"),
15764
- requireBothQuestions: external_exports.boolean().optional(),
15765
- standards: external_exports.array(AssessmentStandardRefSchema2).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
15782
+ requireBothQuestions: external_exports.never().optional(),
15783
+ standards: external_exports.array(AssessmentStandardRefSchema2.and(external_exports.object({ requireBothQuestions: external_exports.boolean().optional() }))).min(1).max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.standards),
15766
15784
  candidateItemsPerStandard: external_exports.number().int().positive().max(TIMEBACK_ASSESSMENT_REVIEW_REQUEST_LIMITS.candidateItemsPerStandard).optional()
15767
15785
  }),
15768
15786
  StartAssessmentBaseSchema.extend({
package/dist/version.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __pcCreateRequire } from 'node:module'; const require = __pcCreateRequire(import.meta.url);
2
2
 
3
3
  // src/version.ts
4
- var cliVersion = false ? "0.0.0-dev" : "0.31.1-beta.2";
4
+ var cliVersion = false ? "0.0.0-dev" : "0.31.1-beta.4";
5
5
  export {
6
6
  cliVersion
7
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "playcademy",
3
- "version": "0.31.1-beta.2",
3
+ "version": "0.31.1-beta.4",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -55,7 +55,7 @@
55
55
  },
56
56
  "dependencies": {
57
57
  "@inquirer/prompts": "^7.8.6",
58
- "@playcademy/sdk": "0.19.1-beta.3",
58
+ "@playcademy/sdk": "0.19.1-beta.5",
59
59
  "chokidar": "^4.0.3",
60
60
  "colorette": "^2.0.20",
61
61
  "commander": "^14.0.1",