playcademy 0.30.1-beta.4 → 0.30.1-beta.5

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.23-beta.3",
45
+ version: "0.0.23-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.23-beta.3",
1211
+ version: "0.0.23-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.30.1-beta.4";
3048
+ var cliVersion = false ? "0.0.0-dev" : "0.30.1-beta.5";
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.23-beta.3",
24
+ version: "0.0.23-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.23-beta.3",
2336
+ version: "0.0.23-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.23-beta.3",
2348
+ version: "0.0.23-beta.4",
2349
2349
  type: "module",
2350
2350
  exports: {
2351
2351
  "./server": {
@@ -6486,7 +6486,7 @@ import { existsSync as existsSync11, mkdirSync as mkdirSync2, readFileSync as re
6486
6486
  import { join as join12 } from "node:path";
6487
6487
 
6488
6488
  // src/version.ts
6489
- var cliVersion = false ? "0.0.0-dev" : "0.30.1-beta.4";
6489
+ var cliVersion = false ? "0.0.0-dev" : "0.30.1-beta.5";
6490
6490
 
6491
6491
  // src/lib/init/database.ts
6492
6492
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
@@ -1,7 +1,7 @@
1
1
  {
2
- "cliVersion": "0.30.1-beta.4",
3
- "sdkVersion": "0.18.1-beta.4",
4
- "runtimeBuildId": "a659d2c5a31e",
5
- "inputFingerprint": "a659d2c5a31e3c3f75bfc6ac029b2c339690012b682b29e9dac4e788fb240e39",
2
+ "cliVersion": "0.30.1-beta.5",
3
+ "sdkVersion": "0.18.1-beta.5",
4
+ "runtimeBuildId": "b64ff09d5440",
5
+ "inputFingerprint": "b64ff09d5440342a33a16389449225167dc6131323473e94aef8d01b1c64a27e",
6
6
  "entry": "index.js"
7
7
  }
@@ -10119,6 +10119,9 @@ function isAssessmentAttemptCompleted(attempt) {
10119
10119
  function isAssessmentAttemptAwaitingAward(attempt) {
10120
10120
  return attempt.inProgress === ASSESSMENT_ATTEMPT_AWAITING_AWARD.inProgress && attempt.scoreStatus === ASSESSMENT_ATTEMPT_AWAITING_AWARD.scoreStatus;
10121
10121
  }
10122
+ function isAssessmentAttemptSuperseded(attempt) {
10123
+ return attempt.inProgress === ASSESSMENT_ATTEMPT_SUPERSEDED.inProgress && attempt.scoreStatus === ASSESSMENT_ATTEMPT_SUPERSEDED.scoreStatus;
10124
+ }
10122
10125
  function classifyAssessmentSubmission(attempt, submissionId) {
10123
10126
  if (isAssessmentAttemptAwaitingAward(attempt) || isAssessmentAttemptCompleted(attempt)) {
10124
10127
  return attempt.submissionId === submissionId ? "replay" : "reject";
@@ -11374,12 +11377,10 @@ function selectAssessmentCourseCandidate(candidates, subjectSpecified) {
11374
11377
  if (candidates.length === 0) {
11375
11378
  return { outcome: "none" };
11376
11379
  }
11377
- const serviceable = candidates.filter((candidate) => candidate.hasLiveTests || candidate.hasOpenAttempt);
11378
- if (serviceable.length === 0) {
11380
+ const eligible = candidates.filter((candidate) => candidate.hasLiveTests);
11381
+ if (eligible.length === 0) {
11379
11382
  return { outcome: "no_eligible_tests" };
11380
11383
  }
11381
- const withOpenAttempt = serviceable.filter((candidate) => candidate.hasOpenAttempt);
11382
- const eligible = withOpenAttempt.length > 0 ? withOpenAttempt : serviceable;
11383
11384
  const subjects = new Set(eligible.map((candidate) => candidate.subject)).size;
11384
11385
  if (!subjectSpecified && subjects > 1) {
11385
11386
  return { outcome: "ambiguous", subjects };
@@ -11391,12 +11392,9 @@ function assessmentAttemptTimestamp(value) {
11391
11392
  const parsed = Date.parse(value);
11392
11393
  return Number.isNaN(parsed) ? 0 : parsed;
11393
11394
  }
11394
- function newestAttempt(left, right, field) {
11395
- const dateDifference = assessmentAttemptTimestamp(right[field]) - assessmentAttemptTimestamp(left[field]);
11396
- return dateDifference || left.attemptId.localeCompare(right.attemptId);
11397
- }
11398
11395
  function compareNewestCompletedAssessmentAttempt(left, right) {
11399
- return newestAttempt(left, right, "scoreDate");
11396
+ const dateDifference = assessmentAttemptTimestamp(right.scoreDate) - assessmentAttemptTimestamp(left.scoreDate);
11397
+ return dateDifference || left.attemptId.localeCompare(right.attemptId);
11400
11398
  }
11401
11399
  function compareAssessmentCatalogOrder(left, right) {
11402
11400
  if (left.assessmentKey !== null && right.assessmentKey !== null) {
@@ -11419,31 +11417,22 @@ function orderRuntimeAssessmentTests(tests) {
11419
11417
  return [...tests].toSorted(compareAssessmentCatalogOrder);
11420
11418
  }
11421
11419
  function selectRuntimeAssessment(liveTests, attempts) {
11422
- const resumable = attempts.filter((attempt) => attempt.resumable !== false && (isAssessmentAttemptOpen(attempt) || isAssessmentAttemptAwaitingAward(attempt))).toSorted((left, right) => newestAttempt(left, right, "updatedAt"));
11423
- if (resumable.length > 0) {
11424
- return {
11425
- kind: "resume",
11426
- attempt: resumable[0],
11427
- additionalResumableAttemptIds: resumable.slice(1).map((candidate) => candidate.attemptId)
11428
- };
11429
- }
11430
11420
  const orderedTests = orderRuntimeAssessmentTests(liveTests);
11431
11421
  if (orderedTests.length === 0) {
11432
11422
  return null;
11433
11423
  }
11434
- const completed = attempts.filter(isAssessmentAttemptCompleted);
11435
- const exposedAssessmentKeys = new Set(attempts.filter((attempt) => isAssessmentAttemptOpen(attempt) || isAssessmentAttemptAwaitingAward(attempt) || isAssessmentAttemptCompleted(attempt)).map((attempt) => attempt.selectedAssessmentKey));
11424
+ const history = attempts.filter((attempt) => isAssessmentAttemptOpen(attempt) || isAssessmentAttemptAwaitingAward(attempt) || isAssessmentAttemptCompleted(attempt) || isAssessmentAttemptSuperseded(attempt));
11425
+ const exposedAssessmentKeys = new Set(history.map((attempt) => attempt.selectedAssessmentKey));
11436
11426
  const unattempted = orderedTests.find((test) => !exposedAssessmentKeys.has(test.assessmentKey));
11437
11427
  if (unattempted) {
11438
- return { kind: "start", test: unattempted, reason: "unattempted" };
11428
+ return { test: unattempted, reason: "unattempted" };
11439
11429
  }
11440
- const latestCompleted = completed.toSorted(compareNewestCompletedAssessmentAttempt)[0];
11441
- const latestTestIndex = latestCompleted ? orderedTests.findIndex((test) => test.assessmentKey === latestCompleted.selectedAssessmentKey) : -1;
11430
+ const latest = history.toSorted(compareNewestCompletedAssessmentAttempt)[0];
11431
+ const latestTestIndex = latest ? orderedTests.findIndex((test) => test.assessmentKey === latest.selectedAssessmentKey) : -1;
11442
11432
  if (latestTestIndex === -1) {
11443
- return { kind: "start", test: orderedTests[0], reason: "first_live" };
11433
+ return { test: orderedTests[0], reason: "first_live" };
11444
11434
  }
11445
11435
  return {
11446
- kind: "start",
11447
11436
  test: orderedTests[(latestTestIndex + 1) % orderedTests.length],
11448
11437
  reason: "round_robin"
11449
11438
  };
@@ -12162,7 +12151,7 @@ function masteryStandardMatcher(requested) {
12162
12151
  return canonicalAuthored !== null && assessmentStandardRefKey(canonicalAuthored) === requestedKey;
12163
12152
  };
12164
12153
  }
12165
- var SCRIPT_SCHEMES, DATA_RASTER_IMAGE_PATTERN, metadataSymbol, parser, SUPPORTED_QTI_INTERACTION_TYPES, INTERACTION_TYPES, BLANK_SENTINEL, MARKED_BLANK_PATTERN, MARKED_BLANK_GLOBAL_PATTERN, MATCH_CORRECT_TEMPLATE, MAP_RESPONSE_TEMPLATE, MAP_RESPONSE_TEMPLATES, RESPONSE_PROCESSING_TEMPLATES, PRIVATE_PLAYABLE_KEYS, trimmedNonEmptyString, finiteNumber2, positiveFiniteNumber, IDENTIFIER_VALUE, DIRECTED_PAIR_VALUE, scoringMatchComparisonSchema, scoringNumericEqualComparisonSchema, scoringNumericEqualRoundedComparisonSchema, scoringRuleSchema, scoringRulesSchema, scoringItemSchema, assessmentScoringArtifactItemsSchema, assessmentScoringArtifactSchema, MATCH_CORRECT_TEMPLATES, COMMON_CORE_MATH_FRAMEWORK_ALIASES, COMMON_CORE_ELA_FRAMEWORK_ALIASES, COMMON_CORE_FRAMEWORK_ALIASES, ASSESSMENT_ATTEMPT_OPEN, ASSESSMENT_ATTEMPT_COMPLETED, ASSESSMENT_ATTEMPT_AWAITING_AWARD, ASSESSMENT_RUNTIME_ERROR_STATUS, GRADE_VALUES, ROUTING_KEY_MAX_LENGTH, ROUTING_RESULT_KEY_MAX_LENGTH, ROUTING_STAGE_LIMIT, ROUTING_TRACK_LIMIT, ROUTING_NODE_LIMIT, ROUTING_TERMINAL_LIMIT, ROUTING_GROUP_LIMIT, ROUTING_PREDICATE_DEPTH_LIMIT, ROUTING_EXPLORATION_STATE_LIMIT, RoutingKeySchema, RoutingResultKeySchema, DiagnosticTrackOutcomeSchema, DiagnosticActivationPredicateSchema, DiagnosticRoutingTransitionSchema, DiagnosticRoutingManifestV1Schema, POINT_RESPONSE_PATTERN, Identity, AssessmentTranscriptSchema, AssessmentTranscriptError, ZERO_WIDTH_CHARACTERS, CURLY_SINGLE_QUOTES, CURLY_DOUBLE_QUOTES, UNICODE_DASHES, ARITHMETIC_OPERATOR_SPACING, THOUSANDS_GROUP, REVIEW_SELECTION_POLICY_VERSION, DEFAULT_REVIEW_SELECTION_POLICY, ASSESSMENT_RUNTIME_FIXTURE_BUNDLE_VERSION, ASSESSMENT_CHILD_RESULT_KIND, ASSESSMENT_CHILD_RESULT_VERSION, IdentitySchema, RecordedAtSchema, ResponseKeySchema, AssessmentResponseValueSchema, AssessmentScoreSchema, AssessmentStandardRefSchema, DiagnosticRoutingLedgerEntryV1Schema, AssessmentChildResultAttemptV1Schema, AssessmentChildResultAdministrationV1Schema, AssessmentChildResultGradingV1Schema, ReviewChildResultContextV1Schema, FixedChildResultContextV1Schema, DiagnosticChildResultContextV1Schema, CommonPayloadShape, ReviewChildResultPayloadV1Schema, FixedChildResultPayloadV1Schema, DiagnosticChildResultPayloadV1Schema, AssessmentChildResultPayloadV1Schema, AssessmentChildResultCommandV1Schema, RuntimeSubjectSchema, RuntimeGradeSchema, OptionalQueryGradeSchema, AssessmentResponseValueSchema2, AssessmentRuntimeIdentitySchema, ResponseKeySchema2, StartAssessmentBaseSchema, AssessmentStandardRefSchema2, LatestAssessmentFilterBaseSchema, LatestAssessmentFiltersSchema, LatestRuntimeAssessmentQuerySchema, StartAssessmentBodySchema, AssessmentPreparationReceiptSchema, StartAssessmentRequestBodySchema, SaveAssessmentBodySchema, RecordAssessmentProgressBodySchema, ResumeAssessmentBodySchema, ResumeRuntimeAssessmentRequestSchema, SubmitAssessmentItemBodySchema, SubmitAssessmentBodySchema, FinalizeAssessmentBodySchema, PrepareRuntimeAssessmentRequestSchema, StartRuntimeAssessmentRequestSchema, SaveRuntimeAssessmentRequestSchema, RecordRuntimeAssessmentProgressRequestSchema, SubmitAssessmentItemRuntimeRequestSchema, SubmitRuntimeAssessmentRequestSchema, FinalizeRuntimeAssessmentRequestSchema;
12154
+ var SCRIPT_SCHEMES, DATA_RASTER_IMAGE_PATTERN, metadataSymbol, parser, SUPPORTED_QTI_INTERACTION_TYPES, INTERACTION_TYPES, BLANK_SENTINEL, MARKED_BLANK_PATTERN, MARKED_BLANK_GLOBAL_PATTERN, MATCH_CORRECT_TEMPLATE, MAP_RESPONSE_TEMPLATE, MAP_RESPONSE_TEMPLATES, RESPONSE_PROCESSING_TEMPLATES, PRIVATE_PLAYABLE_KEYS, trimmedNonEmptyString, finiteNumber2, positiveFiniteNumber, IDENTIFIER_VALUE, DIRECTED_PAIR_VALUE, scoringMatchComparisonSchema, scoringNumericEqualComparisonSchema, scoringNumericEqualRoundedComparisonSchema, scoringRuleSchema, scoringRulesSchema, scoringItemSchema, assessmentScoringArtifactItemsSchema, assessmentScoringArtifactSchema, MATCH_CORRECT_TEMPLATES, COMMON_CORE_MATH_FRAMEWORK_ALIASES, COMMON_CORE_ELA_FRAMEWORK_ALIASES, COMMON_CORE_FRAMEWORK_ALIASES, ASSESSMENT_ATTEMPT_OPEN, ASSESSMENT_ATTEMPT_COMPLETED, ASSESSMENT_ATTEMPT_AWAITING_AWARD, ASSESSMENT_ATTEMPT_SUPERSEDED, ASSESSMENT_RUNTIME_ERROR_STATUS, GRADE_VALUES, ROUTING_KEY_MAX_LENGTH, ROUTING_RESULT_KEY_MAX_LENGTH, ROUTING_STAGE_LIMIT, ROUTING_TRACK_LIMIT, ROUTING_NODE_LIMIT, ROUTING_TERMINAL_LIMIT, ROUTING_GROUP_LIMIT, ROUTING_PREDICATE_DEPTH_LIMIT, ROUTING_EXPLORATION_STATE_LIMIT, RoutingKeySchema, RoutingResultKeySchema, DiagnosticTrackOutcomeSchema, DiagnosticActivationPredicateSchema, DiagnosticRoutingTransitionSchema, DiagnosticRoutingManifestV1Schema, POINT_RESPONSE_PATTERN, Identity, AssessmentTranscriptSchema, AssessmentTranscriptError, ZERO_WIDTH_CHARACTERS, CURLY_SINGLE_QUOTES, CURLY_DOUBLE_QUOTES, UNICODE_DASHES, ARITHMETIC_OPERATOR_SPACING, THOUSANDS_GROUP, REVIEW_SELECTION_POLICY_VERSION, DEFAULT_REVIEW_SELECTION_POLICY, ASSESSMENT_RUNTIME_FIXTURE_BUNDLE_VERSION, ASSESSMENT_CHILD_RESULT_KIND, ASSESSMENT_CHILD_RESULT_VERSION, IdentitySchema, RecordedAtSchema, ResponseKeySchema, AssessmentResponseValueSchema, AssessmentScoreSchema, AssessmentStandardRefSchema, DiagnosticRoutingLedgerEntryV1Schema, AssessmentChildResultAttemptV1Schema, AssessmentChildResultAdministrationV1Schema, AssessmentChildResultGradingV1Schema, ReviewChildResultContextV1Schema, FixedChildResultContextV1Schema, DiagnosticChildResultContextV1Schema, CommonPayloadShape, ReviewChildResultPayloadV1Schema, FixedChildResultPayloadV1Schema, DiagnosticChildResultPayloadV1Schema, AssessmentChildResultPayloadV1Schema, AssessmentChildResultCommandV1Schema, RuntimeSubjectSchema, RuntimeGradeSchema, OptionalQueryGradeSchema, AssessmentResponseValueSchema2, AssessmentRuntimeIdentitySchema, ResponseKeySchema2, StartAssessmentBaseSchema, AssessmentStandardRefSchema2, LatestAssessmentFilterBaseSchema, LatestAssessmentFiltersSchema, LatestRuntimeAssessmentQuerySchema, StartAssessmentBodySchema, AssessmentPreparationReceiptSchema, StartAssessmentRequestBodySchema, SaveAssessmentBodySchema, RecordAssessmentProgressBodySchema, ResumeAssessmentBodySchema, ResumeRuntimeAssessmentRequestSchema, SubmitAssessmentItemBodySchema, SubmitAssessmentBodySchema, FinalizeAssessmentBodySchema, PrepareRuntimeAssessmentRequestSchema, StartRuntimeAssessmentRequestSchema, SaveRuntimeAssessmentRequestSchema, RecordRuntimeAssessmentProgressRequestSchema, SubmitAssessmentItemRuntimeRequestSchema, SubmitRuntimeAssessmentRequestSchema, FinalizeRuntimeAssessmentRequestSchema;
12166
12155
  var init_assessment_runtime2 = __esm({
12167
12156
  "../timeback/dist/engines/assessment-runtime/index.js"() {
12168
12157
  "use strict";
@@ -12359,6 +12348,10 @@ var init_assessment_runtime2 = __esm({
12359
12348
  inProgress: "false",
12360
12349
  scoreStatus: "submitted"
12361
12350
  };
12351
+ ASSESSMENT_ATTEMPT_SUPERSEDED = {
12352
+ inProgress: "false",
12353
+ scoreStatus: "not submitted"
12354
+ };
12362
12355
  ASSESSMENT_RUNTIME_ERROR_STATUS = {
12363
12356
  NO_ELIGIBLE_TESTS: 404,
12364
12357
  REVIEW_BANK_SHORTAGE: 422,
@@ -12768,16 +12761,18 @@ var init_local_assessment_runtime = __esm({
12768
12761
  return { status: "unprepared", reason: "local_runtime" };
12769
12762
  }
12770
12763
  start(studentId2, input) {
12771
- const catalog = this.catalog(input, studentId2);
12764
+ const catalog = this.catalog(input);
12772
12765
  if (input.purpose === "review") {
12773
12766
  return this.startReview(studentId2, input, catalog);
12774
12767
  }
12775
12768
  if (input.purpose === "diagnostic") {
12776
12769
  return this.startDiagnostic(studentId2, input, catalog);
12777
12770
  }
12778
- const attempts = [...this.attempts.values()].filter(
12779
- (attempt2) => attempt2.studentId === studentId2 && attempt2.catalogId === catalog.id && attempt2.activityId === input.activityId
12780
- );
12771
+ const attempts = this.attemptsInScope(studentId2, catalog.id, input.activityId);
12772
+ const pending = this.pendingAward(attempts);
12773
+ if (pending) {
12774
+ return this.snapshot(pending);
12775
+ }
12781
12776
  const compatibleAttempts = attempts.filter(
12782
12777
  (attempt2) => input.purpose !== "mastery" || attempt2.selection.kind === "standard-quiz" && masteryStandardMatches(attempt2.selection.standard, input.standard)
12783
12778
  );
@@ -12789,19 +12784,14 @@ var init_local_assessment_runtime = __esm({
12789
12784
  compatibleAttempts.map((attempt2) => ({
12790
12785
  attemptId: attempt2.attemptId,
12791
12786
  selectedAssessmentKey: attempt2.selectedTest.assessmentKey,
12792
- selectedTestIdentifier: attempt2.selectedTest.identifier,
12793
12787
  inProgress: attempt2.inProgress,
12794
12788
  scoreStatus: attempt2.scoreStatus,
12795
- updatedAt: attempt2.updatedAt,
12796
12789
  scoreDate: attempt2.scoreDate
12797
12790
  }))
12798
12791
  );
12799
12792
  if (!selection) {
12800
12793
  throw failure(assessmentNoEligibleTests(input.purpose));
12801
12794
  }
12802
- if (selection.kind === "resume") {
12803
- return this.snapshot(this.attempts.get(selection.attempt.attemptId));
12804
- }
12805
12795
  const timestamp = this.timestamp();
12806
12796
  const attempt = {
12807
12797
  attemptId: crypto.randomUUID(),
@@ -12826,12 +12816,17 @@ var init_local_assessment_runtime = __esm({
12826
12816
  } : { kind: "fixed-test", purpose: input.purpose }
12827
12817
  };
12828
12818
  this.attempts.set(attempt.attemptId, attempt);
12819
+ this.supersedeReplaced(attempt);
12829
12820
  return this.snapshot(attempt);
12830
12821
  }
12831
12822
  startDiagnostic(studentId2, input, catalog) {
12832
- const attempts = [...this.attempts.values()].filter(
12833
- (attempt2) => attempt2.studentId === studentId2 && attempt2.catalogId === catalog.id && attempt2.activityId === input.activityId && attempt2.selection.kind === "platform-routed-diagnostic" && attempt2.selection.diagnosticKey === input.diagnosticKey
12823
+ const attempts = this.attemptsInScope(studentId2, catalog.id, input.activityId).filter(
12824
+ (attempt2) => attempt2.selection.kind === "platform-routed-diagnostic" && attempt2.selection.diagnosticKey === input.diagnosticKey
12834
12825
  );
12826
+ const pending = this.pendingAward(attempts);
12827
+ if (pending) {
12828
+ return this.snapshot(pending);
12829
+ }
12835
12830
  const liveTests = catalog.tests.filter(
12836
12831
  (test2) => test2.live && test2.diagnostic?.diagnosticKey === input.diagnosticKey
12837
12832
  );
@@ -12840,19 +12835,14 @@ var init_local_assessment_runtime = __esm({
12840
12835
  attempts.map((attempt2) => ({
12841
12836
  attemptId: attempt2.attemptId,
12842
12837
  selectedAssessmentKey: attempt2.selectedTest.assessmentKey,
12843
- selectedTestIdentifier: attempt2.selectedTest.identifier,
12844
12838
  inProgress: attempt2.inProgress,
12845
12839
  scoreStatus: attempt2.scoreStatus,
12846
- updatedAt: attempt2.updatedAt,
12847
12840
  scoreDate: attempt2.scoreDate
12848
12841
  }))
12849
12842
  );
12850
12843
  if (!decision) {
12851
12844
  throw failure(assessmentNoEligibleTests(input.purpose));
12852
12845
  }
12853
- if (decision.kind === "resume") {
12854
- return this.snapshot(this.attempts.get(decision.attempt.attemptId));
12855
- }
12856
12846
  const test = this.fixtureTest(catalog, decision.test.qtiTestIdentifier);
12857
12847
  const diagnostic = test.diagnostic;
12858
12848
  if (!diagnostic || diagnostic.diagnosticKey !== input.diagnosticKey) {
@@ -12900,34 +12890,32 @@ var init_local_assessment_runtime = __esm({
12900
12890
  }
12901
12891
  };
12902
12892
  this.attempts.set(attempt.attemptId, attempt);
12893
+ this.supersedeReplaced(attempt);
12903
12894
  return this.snapshot(attempt);
12904
12895
  }
12905
12896
  startReview(studentId2, input, catalog) {
12906
12897
  const requestFingerprint = reviewRequestFingerprint(input, DEFAULT_REVIEW_SELECTION_POLICY);
12907
12898
  const request2 = normalizeReviewRequest(input);
12908
- const attempts = [...this.attempts.values()].filter(
12909
- (attempt2) => attempt2.studentId === studentId2 && attempt2.catalogId === catalog.id && attempt2.activityId === input.activityId
12910
- );
12911
- const compatible = attempts.filter(
12912
- (attempt2) => attempt2.selection.kind === "standards-review" && attempt2.review?.requestFingerprint === requestFingerprint
12899
+ const attempts = this.attemptsInScope(studentId2, catalog.id, input.activityId);
12900
+ const pending = this.pendingAward(attempts);
12901
+ if (pending) {
12902
+ return this.snapshot(pending);
12903
+ }
12904
+ const reviewAttempts = attempts.filter(
12905
+ (attempt2) => attempt2.selection.kind === "standards-review"
12913
12906
  );
12914
12907
  const liveTests = catalog.tests.filter((test2) => test2.live);
12915
12908
  const decision = selectRuntimeAssessment(
12916
12909
  liveTests,
12917
- compatible.map((attempt2) => ({
12910
+ reviewAttempts.map((attempt2) => ({
12918
12911
  attemptId: attempt2.attemptId,
12919
12912
  selectedAssessmentKey: attempt2.selectedTest.assessmentKey,
12920
- selectedTestIdentifier: attempt2.selectedTest.identifier,
12921
12913
  inProgress: attempt2.inProgress,
12922
12914
  scoreStatus: attempt2.scoreStatus,
12923
- updatedAt: attempt2.updatedAt,
12924
12915
  scoreDate: attempt2.scoreDate
12925
12916
  }))
12926
12917
  );
12927
- if (decision?.kind === "resume") {
12928
- return this.snapshot(this.attempts.get(decision.attempt.attemptId));
12929
- }
12930
- if (!decision || liveTests.length === 0) {
12918
+ if (!decision) {
12931
12919
  throw failure(assessmentNoEligibleTests(input.purpose));
12932
12920
  }
12933
12921
  if (liveTests.length > 1) {
@@ -12981,6 +12969,7 @@ var init_local_assessment_runtime = __esm({
12981
12969
  }
12982
12970
  };
12983
12971
  this.attempts.set(attempt.attemptId, attempt);
12972
+ this.supersedeReplaced(attempt);
12984
12973
  return this.snapshot(attempt);
12985
12974
  }
12986
12975
  get(studentId2, attemptId) {
@@ -13299,8 +13288,7 @@ var init_local_assessment_runtime = __esm({
13299
13288
  attempt.updatedAt = this.timestamp();
13300
13289
  return this.finalizedResult(attempt);
13301
13290
  }
13302
- catalog(input, studentId2) {
13303
- const requestedReviewFingerprint = input.purpose === "review" ? reviewRequestFingerprint(input, DEFAULT_REVIEW_SELECTION_POLICY) : void 0;
13291
+ catalog(input) {
13304
13292
  const courses = /* @__PURE__ */ new Map();
13305
13293
  const matching = this.bundle.catalogs.filter(
13306
13294
  (catalog) => (!input.subject || catalog.subject === input.subject) && (input.grade === void 0 || catalog.grade === input.grade)
@@ -13312,7 +13300,6 @@ var init_local_assessment_runtime = __esm({
13312
13300
  grade: catalog.grade,
13313
13301
  id: catalog.id,
13314
13302
  hasLiveTests: false,
13315
- hasOpenAttempt: false,
13316
13303
  catalog: null
13317
13304
  };
13318
13305
  if (catalog.purpose === input.purpose) {
@@ -13321,12 +13308,6 @@ var init_local_assessment_runtime = __esm({
13321
13308
  entry.hasLiveTests = catalog.tests.some(
13322
13309
  (test) => test.live && (input.purpose !== "mastery" || masteryStandardMatches(test.standard, input.standard)) && (input.purpose !== "diagnostic" || test.diagnostic?.diagnosticKey === input.diagnosticKey)
13323
13310
  );
13324
- entry.hasOpenAttempt = [...this.attempts.values()].some(
13325
- (attempt) => attempt.studentId === studentId2 && attempt.catalogId === catalog.id && attempt.activityId === input.activityId && (input.purpose !== "review" || attempt.selection.kind === "standards-review" && attempt.review?.requestFingerprint === requestedReviewFingerprint) && (input.purpose !== "mastery" || attempt.selection.kind === "standard-quiz" && masteryStandardMatches(
13326
- attempt.selection.standard,
13327
- input.standard
13328
- )) && (input.purpose !== "diagnostic" || attempt.selection.kind === "platform-routed-diagnostic" && attempt.selection.diagnosticKey === input.diagnosticKey) && (isAssessmentAttemptOpen(attempt) || isAssessmentAttemptAwaitingAward(attempt))
13329
- );
13330
13311
  }
13331
13312
  courses.set(key, entry);
13332
13313
  }
@@ -13477,6 +13458,34 @@ var init_local_assessment_runtime = __esm({
13477
13458
  items: bank.items
13478
13459
  };
13479
13460
  }
13461
+ /**
13462
+ * A fresh start replaces the student's open attempts for the same
13463
+ * activity: they close as superseded instead of lingering as resumable,
13464
+ * mirroring the hosted provider. Awaiting-award attempts keep settling.
13465
+ */
13466
+ /** A start hands back the newest attempt whose award is still pending, mirroring the hosted provider. */
13467
+ pendingAward(attempts) {
13468
+ return attempts.filter(isAssessmentAttemptAwaitingAward).toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0] ?? null;
13469
+ }
13470
+ supersedeReplaced(replacement) {
13471
+ for (const attempt of this.attemptsInScope(
13472
+ replacement.studentId,
13473
+ replacement.catalogId,
13474
+ replacement.activityId
13475
+ )) {
13476
+ if (attempt.attemptId !== replacement.attemptId && isAssessmentAttemptOpen(attempt)) {
13477
+ Object.assign(attempt, ASSESSMENT_ATTEMPT_SUPERSEDED, {
13478
+ updatedAt: this.timestamp()
13479
+ });
13480
+ }
13481
+ }
13482
+ }
13483
+ /** One student's attempts for an activity within a catalog (catalogs are per purpose). */
13484
+ attemptsInScope(studentId2, catalogId, activityId) {
13485
+ return [...this.attempts.values()].filter(
13486
+ (attempt) => attempt.studentId === studentId2 && attempt.catalogId === catalogId && attempt.activityId === activityId
13487
+ );
13488
+ }
13480
13489
  reviewExposures(attempts, bankRevision) {
13481
13490
  return attempts.flatMap((attempt) => {
13482
13491
  if (attempt.selection.kind !== "standards-review" || attempt.review?.bankRevision !== bankRevision) {
@@ -13490,6 +13499,11 @@ var init_local_assessment_runtime = __esm({
13490
13499
  }
13491
13500
  snapshot(stored, suppliedTranscript) {
13492
13501
  let attempt = stored;
13502
+ if (isAssessmentAttemptSuperseded(attempt)) {
13503
+ throw new ApiError(410, "GONE", "This assessment attempt has been superseded.", {
13504
+ attemptId: attempt.attemptId
13505
+ });
13506
+ }
13493
13507
  if (isAssessmentAttemptAwaitingAward(attempt)) {
13494
13508
  return {
13495
13509
  attemptId: attempt.attemptId,
@@ -14188,9 +14202,9 @@ var init_list = __esm({
14188
14202
  // ../edge-play/src/game/lib/metadata.ts
14189
14203
  function getRuntimeMetadata() {
14190
14204
  return {
14191
- cliVersion: true ? "0.30.1-beta.4" : "0.0.0-dev",
14192
- sdkVersion: true ? "0.18.1-beta.4" : "0.0.0-dev",
14193
- buildId: true ? "a659d2c5a31e" : "dev-source"
14205
+ cliVersion: true ? "0.30.1-beta.5" : "0.0.0-dev",
14206
+ sdkVersion: true ? "0.18.1-beta.5" : "0.0.0-dev",
14207
+ buildId: true ? "b64ff09d5440" : "dev-source"
14194
14208
  };
14195
14209
  }
14196
14210
  var init_metadata = __esm({
@@ -1,7 +1,7 @@
1
1
  {
2
- "cliVersion": "0.30.1-beta.4",
3
- "sdkVersion": "0.18.1-beta.4",
4
- "runtimeBuildId": "a659d2c5a31e",
5
- "inputFingerprint": "a659d2c5a31e3c3f75bfc6ac029b2c339690012b682b29e9dac4e788fb240e39",
2
+ "cliVersion": "0.30.1-beta.5",
3
+ "sdkVersion": "0.18.1-beta.5",
4
+ "runtimeBuildId": "b64ff09d5440",
5
+ "inputFingerprint": "b64ff09d5440342a33a16389449225167dc6131323473e94aef8d01b1c64a27e",
6
6
  "entry": "index.js"
7
7
  }
package/dist/utils.js CHANGED
@@ -396,7 +396,7 @@ var DASHBOARD_API_ROUTES_DIRECTORY = "api";
396
396
  // ../better-auth/package.json
397
397
  var package_default = {
398
398
  name: "@playcademy/better-auth",
399
- version: "0.0.23-beta.3",
399
+ version: "0.0.23-beta.4",
400
400
  type: "module",
401
401
  exports: {
402
402
  "./server": {
@@ -2849,7 +2849,7 @@ import { existsSync as existsSync9, mkdirSync as mkdirSync2, writeFileSync as wr
2849
2849
  import { dirname as dirname4, join as join13 } from "node:path";
2850
2850
 
2851
2851
  // src/version.ts
2852
- var cliVersion = false ? "0.0.0-dev" : "0.30.1-beta.4";
2852
+ var cliVersion = false ? "0.0.0-dev" : "0.30.1-beta.5";
2853
2853
 
2854
2854
  // src/lib/build/binary-resource.ts
2855
2855
  function writeFileTree(baseDir, files) {
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.30.1-beta.4";
4
+ var cliVersion = false ? "0.0.0-dev" : "0.30.1-beta.5";
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.30.1-beta.4",
3
+ "version": "0.30.1-beta.5",
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.18.1-beta.4",
58
+ "@playcademy/sdk": "0.18.1-beta.5",
59
59
  "chokidar": "^4.0.3",
60
60
  "colorette": "^2.0.20",
61
61
  "commander": "^14.0.1",