playcademy 0.29.0 → 0.29.1-beta.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/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.21",
45
+ version: "0.0.22-beta.2",
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.21",
1211
+ version: "0.0.22-beta.2",
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.29.0";
3048
+ var cliVersion = false ? "0.0.0-dev" : "0.29.1-beta.2";
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.21",
24
+ version: "0.0.22-beta.2",
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.21",
2336
+ version: "0.0.22-beta.2",
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.21",
2348
+ version: "0.0.22-beta.2",
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.29.0";
6489
+ var cliVersion = false ? "0.0.0-dev" : "0.29.1-beta.2";
6490
6490
 
6491
6491
  // src/lib/init/database.ts
6492
6492
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
@@ -22714,6 +22714,15 @@ var SubmitAssessmentBodySchema = external_exports.object({
22714
22714
  message: "Session timing needs a UUID runId and resumeId with non-negative active and inactive seconds"
22715
22715
  }).optional()
22716
22716
  });
22717
+ var FinalizeAssessmentBodySchema = external_exports.object({
22718
+ submissionId: external_exports.string().trim().min(1),
22719
+ xpAwarded: external_exports.number().finite().nonnegative(),
22720
+ masteredUnits: external_exports.number().finite().optional(),
22721
+ masteredUnitsAbsolute: external_exports.number().finite().int().optional()
22722
+ }).refine((body) => body.masteredUnits === void 0 || body.masteredUnitsAbsolute === void 0, {
22723
+ message: "Provide either masteredUnits or masteredUnitsAbsolute, not both",
22724
+ path: ["masteredUnits"]
22725
+ });
22717
22726
  var StartRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.and(StartAssessmentBodySchema);
22718
22727
  var SaveRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(SaveAssessmentBodySchema.shape);
22719
22728
  var SubmitAssessmentItemRuntimeRequestSchema = AssessmentRuntimeIdentitySchema.extend(SubmitAssessmentItemBodySchema.shape);
@@ -22721,6 +22730,10 @@ var SubmitRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.exten
22721
22730
  appName: external_exports.string().trim().min(1),
22722
22731
  sensorUrl: external_exports.string().url()
22723
22732
  });
22733
+ var FinalizeRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend({
22734
+ appName: external_exports.string().trim().min(1),
22735
+ sensorUrl: external_exports.string().url()
22736
+ }).and(FinalizeAssessmentBodySchema);
22724
22737
 
22725
22738
  // src/lib/timeback/assessment-fixtures.ts
22726
22739
  function assessmentFixturePath(workspace = getWorkspace()) {
@@ -1,7 +1,7 @@
1
1
  {
2
- "cliVersion": "0.29.0",
3
- "sdkVersion": "0.17.0",
4
- "runtimeBuildId": "8d696801888c",
5
- "inputFingerprint": "8d696801888c6ba351bc7c899ba3124cdb00864c28c9be0c4fa94eb86f279079",
2
+ "cliVersion": "0.29.1-beta.2",
3
+ "sdkVersion": "0.17.1-beta.2",
4
+ "runtimeBuildId": "1fbde6bc12e9",
5
+ "inputFingerprint": "1fbde6bc12e961a9f80f44a0d8603ee681b92c647c286a60e67606bc1020ab70",
6
6
  "entry": "index.js"
7
7
  }
@@ -10058,12 +10058,30 @@ function isAssessmentAttemptOpen(attempt) {
10058
10058
  function isAssessmentAttemptCompleted(attempt) {
10059
10059
  return attempt.inProgress === ASSESSMENT_ATTEMPT_COMPLETED.inProgress && attempt.scoreStatus === ASSESSMENT_ATTEMPT_COMPLETED.scoreStatus;
10060
10060
  }
10061
+ function isAssessmentAttemptAwaitingAward(attempt) {
10062
+ return attempt.inProgress === ASSESSMENT_ATTEMPT_AWAITING_AWARD.inProgress && attempt.scoreStatus === ASSESSMENT_ATTEMPT_AWAITING_AWARD.scoreStatus;
10063
+ }
10061
10064
  function classifyAssessmentSubmission(attempt, submissionId) {
10062
- if (isAssessmentAttemptCompleted(attempt)) {
10065
+ if (isAssessmentAttemptAwaitingAward(attempt)) {
10063
10066
  return attempt.submissionId === submissionId ? "replay" : "reject";
10064
10067
  }
10068
+ if (isAssessmentAttemptCompleted(attempt)) {
10069
+ return "reject";
10070
+ }
10065
10071
  return isAssessmentAttemptOpen(attempt) ? "submit" : "reject";
10066
10072
  }
10073
+ function isSameAssessmentAward(recorded, input) {
10074
+ return recorded.xpAwarded === input.xpAwarded && recorded.masteredUnits === input.masteredUnits && recorded.masteredUnitsAbsolute === input.masteredUnitsAbsolute;
10075
+ }
10076
+ function classifyAssessmentFinalization(attempt, input) {
10077
+ if (isAssessmentAttemptCompleted(attempt)) {
10078
+ return attempt.submissionId === input.submissionId && attempt.award !== void 0 && isSameAssessmentAward(attempt.award, input) ? "replay" : "conflict";
10079
+ }
10080
+ if (!isAssessmentAttemptAwaitingAward(attempt) || attempt.submissionId !== input.submissionId) {
10081
+ return "reject";
10082
+ }
10083
+ return "finalize";
10084
+ }
10067
10085
  function assessmentFlowViolation(message2, details) {
10068
10086
  return {
10069
10087
  code: "ASSESSMENT_FLOW_VIOLATION",
@@ -10092,6 +10110,13 @@ function assessmentAlreadySubmitted(attemptId) {
10092
10110
  details: { attemptId }
10093
10111
  };
10094
10112
  }
10113
+ function assessmentAwardConflict(details) {
10114
+ return {
10115
+ code: "ASSESSMENT_FLOW_VIOLATION",
10116
+ message: "This assessment award conflicts with the recorded finalization.",
10117
+ details
10118
+ };
10119
+ }
10095
10120
  function assessmentResponseVersionConflict(expectedResponseVersion, responseVersion) {
10096
10121
  return {
10097
10122
  code: "RESPONSE_VERSION_CONFLICT",
@@ -11352,7 +11377,7 @@ function orderRuntimeAssessmentTests(tests) {
11352
11377
  return [...tests].toSorted(compareAssessmentCatalogOrder);
11353
11378
  }
11354
11379
  function selectRuntimeAssessment(liveTests, attempts) {
11355
- const resumable = attempts.filter((attempt) => attempt.resumable !== false && isAssessmentAttemptOpen(attempt)).toSorted((left, right) => newestAttempt(left, right, "updatedAt"));
11380
+ const resumable = attempts.filter((attempt) => attempt.resumable !== false && (isAssessmentAttemptOpen(attempt) || isAssessmentAttemptAwaitingAward(attempt))).toSorted((left, right) => newestAttempt(left, right, "updatedAt"));
11356
11381
  if (resumable.length > 0) {
11357
11382
  return {
11358
11383
  kind: "resume",
@@ -11365,7 +11390,7 @@ function selectRuntimeAssessment(liveTests, attempts) {
11365
11390
  return null;
11366
11391
  }
11367
11392
  const completed = attempts.filter(isAssessmentAttemptCompleted);
11368
- const exposedAssessmentKeys = new Set(attempts.filter((attempt) => isAssessmentAttemptOpen(attempt) || isAssessmentAttemptCompleted(attempt)).map((attempt) => attempt.selectedAssessmentKey));
11393
+ const exposedAssessmentKeys = new Set(attempts.filter((attempt) => isAssessmentAttemptOpen(attempt) || isAssessmentAttemptAwaitingAward(attempt) || isAssessmentAttemptCompleted(attempt)).map((attempt) => attempt.selectedAssessmentKey));
11369
11394
  const unattempted = orderedTests.find((test) => !exposedAssessmentKeys.has(test.assessmentKey));
11370
11395
  if (unattempted) {
11371
11396
  return { kind: "start", test: unattempted, reason: "unattempted" };
@@ -11927,7 +11952,7 @@ function masteryStandardMatcher(requested) {
11927
11952
  return canonicalAuthored !== null && assessmentStandardRefKey(canonicalAuthored) === requestedKey;
11928
11953
  };
11929
11954
  }
11930
- 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, COMMON_CORE_MATH_FRAMEWORK_ALIASES, COMMON_CORE_ELA_FRAMEWORK_ALIASES, COMMON_CORE_FRAMEWORK_ALIASES, ASSESSMENT_ATTEMPT_OPEN, ASSESSMENT_ATTEMPT_COMPLETED, ASSESSMENT_RUNTIME_ERROR_STATUS, 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, GRADE_VALUES, POINT_RESPONSE_PATTERN, REVIEW_SELECTION_POLICY_VERSION, DEFAULT_REVIEW_SELECTION_POLICY, ASSESSMENT_RUNTIME_FIXTURE_BUNDLE_VERSION, RuntimeSubjectSchema, RuntimeGradeSchema, OptionalQueryGradeSchema, AssessmentResponseValueSchema, AssessmentRuntimeIdentitySchema, ResponseKeySchema, StartAssessmentBaseSchema, AssessmentStandardRefSchema, LatestAssessmentFilterBaseSchema, LatestAssessmentFiltersSchema, LatestRuntimeAssessmentQuerySchema, StartAssessmentBodySchema, SaveAssessmentBodySchema, SubmitAssessmentItemBodySchema, SubmitAssessmentBodySchema, StartRuntimeAssessmentRequestSchema, SaveRuntimeAssessmentRequestSchema, SubmitAssessmentItemRuntimeRequestSchema, SubmitRuntimeAssessmentRequestSchema;
11955
+ 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, 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, 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, GRADE_VALUES, POINT_RESPONSE_PATTERN, REVIEW_SELECTION_POLICY_VERSION, DEFAULT_REVIEW_SELECTION_POLICY, ASSESSMENT_RUNTIME_FIXTURE_BUNDLE_VERSION, RuntimeSubjectSchema, RuntimeGradeSchema, OptionalQueryGradeSchema, AssessmentResponseValueSchema, AssessmentRuntimeIdentitySchema, ResponseKeySchema, StartAssessmentBaseSchema, AssessmentStandardRefSchema, LatestAssessmentFilterBaseSchema, LatestAssessmentFiltersSchema, LatestRuntimeAssessmentQuerySchema, StartAssessmentBodySchema, SaveAssessmentBodySchema, SubmitAssessmentItemBodySchema, SubmitAssessmentBodySchema, FinalizeAssessmentBodySchema, StartRuntimeAssessmentRequestSchema, SaveRuntimeAssessmentRequestSchema, SubmitAssessmentItemRuntimeRequestSchema, SubmitRuntimeAssessmentRequestSchema, FinalizeRuntimeAssessmentRequestSchema;
11931
11956
  var init_assessment_runtime2 = __esm({
11932
11957
  "../timeback/dist/engines/assessment-runtime/index.js"() {
11933
11958
  "use strict";
@@ -12021,6 +12046,10 @@ var init_assessment_runtime2 = __esm({
12021
12046
  inProgress: "false",
12022
12047
  scoreStatus: "fully graded"
12023
12048
  };
12049
+ ASSESSMENT_ATTEMPT_AWAITING_AWARD = {
12050
+ inProgress: "false",
12051
+ scoreStatus: "submitted"
12052
+ };
12024
12053
  ASSESSMENT_RUNTIME_ERROR_STATUS = {
12025
12054
  NO_ELIGIBLE_TESTS: 404,
12026
12055
  REVIEW_BANK_SHORTAGE: 422,
@@ -12187,6 +12216,15 @@ var init_assessment_runtime2 = __esm({
12187
12216
  message: "Session timing needs a UUID runId and resumeId with non-negative active and inactive seconds"
12188
12217
  }).optional()
12189
12218
  });
12219
+ FinalizeAssessmentBodySchema = external_exports.object({
12220
+ submissionId: external_exports.string().trim().min(1),
12221
+ xpAwarded: external_exports.number().finite().nonnegative(),
12222
+ masteredUnits: external_exports.number().finite().optional(),
12223
+ masteredUnitsAbsolute: external_exports.number().finite().int().optional()
12224
+ }).refine((body) => body.masteredUnits === void 0 || body.masteredUnitsAbsolute === void 0, {
12225
+ message: "Provide either masteredUnits or masteredUnitsAbsolute, not both",
12226
+ path: ["masteredUnits"]
12227
+ });
12190
12228
  StartRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.and(StartAssessmentBodySchema);
12191
12229
  SaveRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(SaveAssessmentBodySchema.shape);
12192
12230
  SubmitAssessmentItemRuntimeRequestSchema = AssessmentRuntimeIdentitySchema.extend(SubmitAssessmentItemBodySchema.shape);
@@ -12194,6 +12232,10 @@ var init_assessment_runtime2 = __esm({
12194
12232
  appName: external_exports.string().trim().min(1),
12195
12233
  sensorUrl: external_exports.string().url()
12196
12234
  });
12235
+ FinalizeRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend({
12236
+ appName: external_exports.string().trim().min(1),
12237
+ sensorUrl: external_exports.string().url()
12238
+ }).and(FinalizeAssessmentBodySchema);
12197
12239
  }
12198
12240
  });
12199
12241
 
@@ -12221,6 +12263,12 @@ var init_local_assessment_runtime = __esm({
12221
12263
  LocalAssessmentRuntime = class {
12222
12264
  bundle;
12223
12265
  attempts = /* @__PURE__ */ new Map();
12266
+ /**
12267
+ * Running mastered-unit total per student so absolute awards resolve to
12268
+ * a delta the way the platform does. Local fixtures configure no
12269
+ * masterable ceiling, so nothing is bounded and no percentage is echoed.
12270
+ */
12271
+ masteredUnitsByStudent = /* @__PURE__ */ new Map();
12224
12272
  lastTimestampMs = 0;
12225
12273
  constructor(bundle) {
12226
12274
  this.bundle = bundle;
@@ -12747,7 +12795,7 @@ var init_local_assessment_runtime = __esm({
12747
12795
  const attempt = this.authorize(studentId2, attemptId);
12748
12796
  const submissionDisposition = classifyAssessmentSubmission(attempt, input.submissionId);
12749
12797
  if (submissionDisposition === "replay") {
12750
- return this.completedResult(attempt);
12798
+ return this.submittedResult(attempt);
12751
12799
  }
12752
12800
  if (submissionDisposition === "reject") {
12753
12801
  throw failure(assessmentAlreadySubmitted(attemptId));
@@ -12779,11 +12827,11 @@ var init_local_assessment_runtime = __esm({
12779
12827
  attempt.score = scoreAssessmentFixture(test, attempt.responses);
12780
12828
  }
12781
12829
  attempt.submissionId = input.submissionId;
12782
- attempt.inProgress = ASSESSMENT_ATTEMPT_COMPLETED.inProgress;
12783
- attempt.scoreStatus = ASSESSMENT_ATTEMPT_COMPLETED.scoreStatus;
12830
+ attempt.inProgress = ASSESSMENT_ATTEMPT_AWAITING_AWARD.inProgress;
12831
+ attempt.scoreStatus = ASSESSMENT_ATTEMPT_AWAITING_AWARD.scoreStatus;
12784
12832
  attempt.updatedAt = this.timestamp();
12785
12833
  attempt.scoreDate = attempt.updatedAt;
12786
- return this.completedResult(attempt);
12834
+ return this.submittedResult(attempt);
12787
12835
  }
12788
12836
  finalizeDiagnostic(attempt, submissionId) {
12789
12837
  const diagnostic = this.requireDiagnosticAttempt(attempt);
@@ -12814,11 +12862,47 @@ var init_local_assessment_runtime = __esm({
12814
12862
  }
12815
12863
  attempt.score = aggregateFixtureItemScores(diagnostic.commits.map((commit) => commit.scoring));
12816
12864
  attempt.submissionId = submissionId;
12865
+ attempt.inProgress = ASSESSMENT_ATTEMPT_AWAITING_AWARD.inProgress;
12866
+ attempt.scoreStatus = ASSESSMENT_ATTEMPT_AWAITING_AWARD.scoreStatus;
12867
+ attempt.updatedAt = this.timestamp();
12868
+ attempt.scoreDate = attempt.updatedAt;
12869
+ return this.submittedDiagnosticResult(attempt, tracks);
12870
+ }
12871
+ finalize(studentId2, attemptId, input) {
12872
+ const attempt = this.authorize(studentId2, attemptId);
12873
+ switch (classifyAssessmentFinalization(attempt, input)) {
12874
+ case "replay": {
12875
+ return this.finalizedResult(attempt);
12876
+ }
12877
+ case "conflict": {
12878
+ throw failure(
12879
+ assessmentAwardConflict({
12880
+ attemptId,
12881
+ submissionId: input.submissionId,
12882
+ xpAwarded: input.xpAwarded,
12883
+ ...input.masteredUnits !== void 0 ? { masteredUnits: input.masteredUnits } : {},
12884
+ ...input.masteredUnitsAbsolute !== void 0 ? { masteredUnitsAbsolute: input.masteredUnitsAbsolute } : {}
12885
+ })
12886
+ );
12887
+ }
12888
+ case "reject": {
12889
+ throw failure(assessmentAlreadySubmitted(attemptId));
12890
+ }
12891
+ }
12892
+ const masteryKey = `${studentId2}\0${attempt.catalogId}`;
12893
+ const currentMasteredUnits = this.masteredUnitsByStudent.get(masteryKey) ?? 0;
12894
+ const masteredUnitsApplied = input.masteredUnitsAbsolute !== void 0 ? input.masteredUnitsAbsolute - currentMasteredUnits : input.masteredUnits ?? 0;
12895
+ this.masteredUnitsByStudent.set(masteryKey, currentMasteredUnits + masteredUnitsApplied);
12896
+ attempt.award = {
12897
+ xpAwarded: input.xpAwarded,
12898
+ ...input.masteredUnits !== void 0 ? { masteredUnits: input.masteredUnits } : {},
12899
+ ...input.masteredUnitsAbsolute !== void 0 ? { masteredUnitsAbsolute: input.masteredUnitsAbsolute } : {},
12900
+ masteredUnitsApplied
12901
+ };
12817
12902
  attempt.inProgress = ASSESSMENT_ATTEMPT_COMPLETED.inProgress;
12818
12903
  attempt.scoreStatus = ASSESSMENT_ATTEMPT_COMPLETED.scoreStatus;
12819
12904
  attempt.updatedAt = this.timestamp();
12820
- attempt.scoreDate = attempt.updatedAt;
12821
- return this.completedDiagnosticResult(attempt, tracks);
12905
+ return this.finalizedResult(attempt);
12822
12906
  }
12823
12907
  catalog(input, studentId2) {
12824
12908
  const requestedReviewFingerprint = input.purpose === "review" ? reviewRequestFingerprint(input, DEFAULT_REVIEW_SELECTION_POLICY) : void 0;
@@ -12846,7 +12930,7 @@ var init_local_assessment_runtime = __esm({
12846
12930
  (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(
12847
12931
  attempt.selection.standard,
12848
12932
  input.standard
12849
- )) && (input.purpose !== "diagnostic" || attempt.selection.kind === "platform-routed-diagnostic" && attempt.selection.diagnosticKey === input.diagnosticKey) && isAssessmentAttemptOpen(attempt)
12933
+ )) && (input.purpose !== "diagnostic" || attempt.selection.kind === "platform-routed-diagnostic" && attempt.selection.diagnosticKey === input.diagnosticKey) && (isAssessmentAttemptOpen(attempt) || isAssessmentAttemptAwaitingAward(attempt))
12850
12934
  );
12851
12935
  }
12852
12936
  courses.set(key, entry);
@@ -13010,7 +13094,22 @@ var init_local_assessment_runtime = __esm({
13010
13094
  });
13011
13095
  }
13012
13096
  snapshot(attempt) {
13013
- const completed = isAssessmentAttemptCompleted(attempt);
13097
+ if (isAssessmentAttemptAwaitingAward(attempt)) {
13098
+ return {
13099
+ attemptId: attempt.attemptId,
13100
+ responseVersion: attempt.responseVersion,
13101
+ status: "awaiting_award",
13102
+ result: this.submittedResult(attempt)
13103
+ };
13104
+ }
13105
+ if (isAssessmentAttemptCompleted(attempt)) {
13106
+ return {
13107
+ attemptId: attempt.attemptId,
13108
+ responseVersion: attempt.responseVersion,
13109
+ status: "completed",
13110
+ result: this.finalizedResult(attempt)
13111
+ };
13112
+ }
13014
13113
  const catalog = this.catalogForAttempt(attempt);
13015
13114
  const test = this.testForAttempt(attempt);
13016
13115
  if (attempt.selection.kind === "platform-routed-diagnostic") {
@@ -13027,7 +13126,7 @@ var init_local_assessment_runtime = __esm({
13027
13126
  return {
13028
13127
  attemptId: attempt.attemptId,
13029
13128
  responseVersion: attempt.responseVersion,
13030
- status: completed ? "completed" : "in_progress",
13129
+ status: "in_progress",
13031
13130
  flow: "platform-routed-item-submit",
13032
13131
  activityData: {
13033
13132
  activityId: attempt.activityId,
@@ -13038,13 +13137,13 @@ var init_local_assessment_runtime = __esm({
13038
13137
  assessment: assessmentPresentationForAttempt(test.assessment, attempt.attemptId),
13039
13138
  selection: structuredClone(attempt.selection),
13040
13139
  routing: this.diagnosticRoutingSnapshot(attempt, replayed.state),
13041
- completion: completed ? this.completedResult(attempt) : null
13140
+ completion: null
13042
13141
  };
13043
13142
  }
13044
13143
  return {
13045
13144
  attemptId: attempt.attemptId,
13046
13145
  responseVersion: attempt.responseVersion,
13047
- status: completed ? "completed" : "in_progress",
13146
+ status: "in_progress",
13048
13147
  flow: assessmentFlowForSelection(attempt.selection),
13049
13148
  activityData: {
13050
13149
  activityId: attempt.activityId,
@@ -13057,11 +13156,11 @@ var init_local_assessment_runtime = __esm({
13057
13156
  assessment: assessmentPresentationForAttempt(test.assessment, attempt.attemptId),
13058
13157
  responses: structuredClone(attempt.responses),
13059
13158
  itemSubmissions: structuredClone(attempt.itemSubmissions),
13060
- score: completed ? structuredClone(this.scoreForAttempt(attempt)) : null,
13159
+ score: null,
13061
13160
  selection: structuredClone(attempt.selection)
13062
13161
  };
13063
13162
  }
13064
- completedResult(attempt) {
13163
+ submittedResult(attempt) {
13065
13164
  if (attempt.selection.kind === "platform-routed-diagnostic") {
13066
13165
  const diagnostic = this.requireDiagnosticAttempt(attempt);
13067
13166
  const { routingManifest } = this.diagnosticTestForAttempt(attempt);
@@ -13070,12 +13169,13 @@ var init_local_assessment_runtime = __esm({
13070
13169
  if (!tracks) {
13071
13170
  throw new Error("A completed diagnostic must have terminal track results");
13072
13171
  }
13073
- return this.completedDiagnosticResult(attempt, tracks);
13172
+ return this.submittedDiagnosticResult(attempt, tracks);
13074
13173
  }
13075
13174
  const result = {
13076
13175
  attemptId: attempt.attemptId,
13176
+ submissionId: attempt.submissionId,
13077
13177
  responseVersion: attempt.responseVersion,
13078
- status: "completed",
13178
+ status: "awaiting_award",
13079
13179
  responses: structuredClone(attempt.responses),
13080
13180
  score: structuredClone(this.scoreForAttempt(attempt))
13081
13181
  };
@@ -13090,17 +13190,29 @@ var init_local_assessment_runtime = __esm({
13090
13190
  }
13091
13191
  return { ...result, purpose: attempt.selection.purpose };
13092
13192
  }
13093
- completedDiagnosticResult(attempt, tracks) {
13193
+ submittedDiagnosticResult(attempt, tracks) {
13094
13194
  const diagnostic = this.requireDiagnosticAttempt(attempt);
13095
13195
  return {
13096
13196
  purpose: "diagnostic",
13097
13197
  attemptId: attempt.attemptId,
13198
+ submissionId: attempt.submissionId,
13098
13199
  diagnosticKey: diagnostic.diagnosticKey,
13099
13200
  responseVersion: attempt.responseVersion,
13100
- status: "completed",
13201
+ status: "awaiting_award",
13101
13202
  tracks: structuredClone([...tracks])
13102
13203
  };
13103
13204
  }
13205
+ finalizedResult(attempt) {
13206
+ if (attempt.award === void 0) {
13207
+ throw new Error("A completed assessment must have a recorded award");
13208
+ }
13209
+ return {
13210
+ ...this.submittedResult(attempt),
13211
+ status: "completed",
13212
+ xpAwarded: attempt.award.xpAwarded,
13213
+ masteredUnitsApplied: attempt.award.masteredUnitsApplied
13214
+ };
13215
+ }
13104
13216
  diagnosticRoutingSnapshot(attempt, state) {
13105
13217
  const diagnostic = this.requireDiagnosticAttempt(attempt);
13106
13218
  return projectDiagnosticRoutingSnapshot(diagnostic.routingRevision, state);
@@ -13351,6 +13463,7 @@ var init_shared = __esm({
13351
13463
  // ../edge-play/src/game/routes/integrations/timeback/assessment-fixture-runtime.ts
13352
13464
  var assessment_fixture_runtime_exports = {};
13353
13465
  __export(assessment_fixture_runtime_exports, {
13466
+ FINALIZE: () => FINALIZE,
13354
13467
  GET: () => GET2,
13355
13468
  LATEST: () => LATEST,
13356
13469
  SAVE: () => SAVE,
@@ -13422,6 +13535,12 @@ async function SUBMIT(c) {
13422
13535
  return provider(c).submit(studentId(c), c.req.param("attemptId"), input);
13423
13536
  });
13424
13537
  }
13538
+ async function FINALIZE(c) {
13539
+ return respond(c, RESPOND_ERROR, async () => {
13540
+ const input = parseBody2(FinalizeAssessmentBodySchema, await c.req.json());
13541
+ return provider(c).finalize(studentId(c), c.req.param("attemptId"), input);
13542
+ });
13543
+ }
13425
13544
  var fixtureSource, fixtureProvider, RESPOND_ERROR;
13426
13545
  var init_assessment_fixture_runtime = __esm({
13427
13546
  "../edge-play/src/game/routes/integrations/timeback/assessment-fixture-runtime.ts"() {
@@ -13440,6 +13559,7 @@ var init_assessment_fixture_runtime = __esm({
13440
13559
  // ../edge-play/src/game/routes/integrations/timeback/assessment-runtime.ts
13441
13560
  var assessment_runtime_exports = {};
13442
13561
  __export(assessment_runtime_exports, {
13562
+ FINALIZE: () => FINALIZE2,
13443
13563
  GET: () => GET3,
13444
13564
  LATEST: () => LATEST2,
13445
13565
  SAVE: () => SAVE2,
@@ -13519,6 +13639,20 @@ async function SUBMIT2(c) {
13519
13639
  }
13520
13640
  );
13521
13641
  }
13642
+ async function FINALIZE2(c) {
13643
+ return respond(
13644
+ c,
13645
+ { tag: "TimeBack Assessment Finalize", fallback: "Failed to finalize assessment" },
13646
+ async () => {
13647
+ const studentId2 = requireTimebackStudentId(c);
13648
+ assertTimebackIntegrated(c);
13649
+ const input = await c.req.json();
13650
+ return c.get("sdk").timeback.assessments.finalize(studentId2, c.req.param("attemptId"), input, {
13651
+ sensorUrl: new URL(c.req.url).origin
13652
+ });
13653
+ }
13654
+ );
13655
+ }
13522
13656
  var init_assessment_runtime3 = __esm({
13523
13657
  "../edge-play/src/game/routes/integrations/timeback/assessment-runtime.ts"() {
13524
13658
  "use strict";
@@ -13580,9 +13714,9 @@ var init_list = __esm({
13580
13714
  // ../edge-play/src/game/lib/metadata.ts
13581
13715
  function getRuntimeMetadata() {
13582
13716
  return {
13583
- cliVersion: true ? "0.29.0" : "0.0.0-dev",
13584
- sdkVersion: true ? "0.17.0" : "0.0.0-dev",
13585
- buildId: true ? "8d696801888c" : "dev-source"
13717
+ cliVersion: true ? "0.29.1-beta.2" : "0.0.0-dev",
13718
+ sdkVersion: true ? "0.17.1-beta.2" : "0.0.0-dev",
13719
+ buildId: true ? "1fbde6bc12e9" : "dev-source"
13586
13720
  };
13587
13721
  }
13588
13722
  var init_metadata = __esm({
@@ -15796,6 +15930,17 @@ function createTimebackNamespace(client) {
15796
15930
  appName: client.config.name,
15797
15931
  sensorUrl: context.sensorUrl
15798
15932
  }
15933
+ ),
15934
+ finalize: (studentId2, attemptId, input, context) => client["request"](
15935
+ `/api/timeback/assessments/${encodeURIComponent(attemptId)}/finalize`,
15936
+ "POST",
15937
+ {
15938
+ ...input,
15939
+ gameId: client.gameId,
15940
+ studentId: studentId2,
15941
+ appName: client.config.name,
15942
+ sensorUrl: context.sensorUrl
15943
+ }
15799
15944
  )
15800
15945
  },
15801
15946
  /**
@@ -16656,6 +16801,7 @@ function registerAssessmentRoutes(app, assessments) {
16656
16801
  app.post(`${GAME_ROUTES.TIMEBACK.ASSESSMENTS}/:attemptId/save`, assessments.SAVE);
16657
16802
  app.post(`${GAME_ROUTES.TIMEBACK.ASSESSMENTS}/:attemptId/submit-item`, assessments.SUBMIT_ITEM);
16658
16803
  app.post(`${GAME_ROUTES.TIMEBACK.ASSESSMENTS}/:attemptId/submit`, assessments.SUBMIT);
16804
+ app.post(`${GAME_ROUTES.TIMEBACK.ASSESSMENTS}/:attemptId/finalize`, assessments.FINALIZE);
16659
16805
  }
16660
16806
  async function timebackNotConfiguredStub(c) {
16661
16807
  return c.json({ __playcademyDevWarning: "timeback-not-configured" });
@@ -16767,6 +16913,7 @@ async function registerBuiltinRoutes(app, integrations, hasAssessmentFixtures =
16767
16913
  app.post(`${assessmentsBase}/:attemptId/save`, timebackNotConfiguredStub);
16768
16914
  app.post(`${assessmentsBase}/:attemptId/submit-item`, timebackNotConfiguredStub);
16769
16915
  app.post(`${assessmentsBase}/:attemptId/submit`, timebackNotConfiguredStub);
16916
+ app.post(`${assessmentsBase}/:attemptId/finalize`, timebackNotConfiguredStub);
16770
16917
  }
16771
16918
  }
16772
16919
  if (assessments) {
@@ -1,7 +1,7 @@
1
1
  {
2
- "cliVersion": "0.29.0",
3
- "sdkVersion": "0.17.0",
4
- "runtimeBuildId": "8d696801888c",
5
- "inputFingerprint": "8d696801888c6ba351bc7c899ba3124cdb00864c28c9be0c4fa94eb86f279079",
2
+ "cliVersion": "0.29.1-beta.2",
3
+ "sdkVersion": "0.17.1-beta.2",
4
+ "runtimeBuildId": "1fbde6bc12e9",
5
+ "inputFingerprint": "1fbde6bc12e961a9f80f44a0d8603ee681b92c647c286a60e67606bc1020ab70",
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.21",
399
+ version: "0.0.22-beta.2",
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.29.0";
2852
+ var cliVersion = false ? "0.0.0-dev" : "0.29.1-beta.2";
2853
2853
 
2854
2854
  // src/lib/build/binary-resource.ts
2855
2855
  function writeFileTree(baseDir, files) {
@@ -15482,6 +15482,15 @@ var SubmitAssessmentBodySchema = external_exports.object({
15482
15482
  message: "Session timing needs a UUID runId and resumeId with non-negative active and inactive seconds"
15483
15483
  }).optional()
15484
15484
  });
15485
+ var FinalizeAssessmentBodySchema = external_exports.object({
15486
+ submissionId: external_exports.string().trim().min(1),
15487
+ xpAwarded: external_exports.number().finite().nonnegative(),
15488
+ masteredUnits: external_exports.number().finite().optional(),
15489
+ masteredUnitsAbsolute: external_exports.number().finite().int().optional()
15490
+ }).refine((body) => body.masteredUnits === void 0 || body.masteredUnitsAbsolute === void 0, {
15491
+ message: "Provide either masteredUnits or masteredUnitsAbsolute, not both",
15492
+ path: ["masteredUnits"]
15493
+ });
15485
15494
  var StartRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.and(StartAssessmentBodySchema);
15486
15495
  var SaveRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(SaveAssessmentBodySchema.shape);
15487
15496
  var SubmitAssessmentItemRuntimeRequestSchema = AssessmentRuntimeIdentitySchema.extend(SubmitAssessmentItemBodySchema.shape);
@@ -15489,6 +15498,10 @@ var SubmitRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.exten
15489
15498
  appName: external_exports.string().trim().min(1),
15490
15499
  sensorUrl: external_exports.string().url()
15491
15500
  });
15501
+ var FinalizeRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend({
15502
+ appName: external_exports.string().trim().min(1),
15503
+ sensorUrl: external_exports.string().url()
15504
+ }).and(FinalizeAssessmentBodySchema);
15492
15505
 
15493
15506
  // src/lib/timeback/assessment-fixtures.ts
15494
15507
  function assessmentFixturePath(workspace = getWorkspace()) {
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.29.0";
4
+ var cliVersion = false ? "0.0.0-dev" : "0.29.1-beta.2";
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.29.0",
3
+ "version": "0.29.1-beta.2",
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.17.0",
58
+ "@playcademy/sdk": "0.17.1-beta.2",
59
59
  "chokidar": "^4.0.3",
60
60
  "colorette": "^2.0.20",
61
61
  "commander": "^14.0.1",