@playcademy/sandbox 0.7.1-beta.13 → 0.7.1-beta.15

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.
Files changed (3) hide show
  1. package/dist/cli.js +178 -79
  2. package/dist/server.js +178 -79
  3. package/package.json +1 -1
package/dist/server.js CHANGED
@@ -1122,7 +1122,7 @@ var package_default;
1122
1122
  var init_package = __esm(() => {
1123
1123
  package_default = {
1124
1124
  name: "@playcademy/sandbox",
1125
- version: "0.7.1-beta.13",
1125
+ version: "0.7.1-beta.15",
1126
1126
  description: "Local development server for Playcademy game development",
1127
1127
  type: "module",
1128
1128
  exports: {
@@ -9491,9 +9491,18 @@ function assessmentReviewBankShortage(fulfillment) {
9491
9491
  function assessmentFlowForPurpose(purpose) {
9492
9492
  return purpose === "review" || purpose === "mastery" ? "item-submit" : "attempt-submit";
9493
9493
  }
9494
+ function isObject(value) {
9495
+ return typeof value === "object" && value !== null;
9496
+ }
9494
9497
  function isTimebackGrade(value) {
9495
9498
  return typeof value === "number" && Number.isInteger(value) && GRADE_VALUES.includes(value);
9496
9499
  }
9500
+ function isAssessmentSessionTiming(value) {
9501
+ return isObject(value) && typeof value.runId === "string" && isValidUUID(value.runId) && typeof value.resumeId === "string" && isValidUUID(value.resumeId) && isNonNegativeDuration(value.activeSeconds) && (value.inactiveSeconds === undefined || isNonNegativeDuration(value.inactiveSeconds));
9502
+ }
9503
+ function isNonNegativeDuration(value) {
9504
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
9505
+ }
9497
9506
  function isAssessmentPurpose(value) {
9498
9507
  return ASSESSMENT_PURPOSES.includes(value);
9499
9508
  }
@@ -10337,7 +10346,7 @@ function isPlaycademyAssessmentResultMetadataV1(value) {
10337
10346
  const selectedTest = metadata2.selectedTest;
10338
10347
  const mastery = metadata2.mastery;
10339
10348
  const review = metadata2.review;
10340
- return metadata2.version === 1 && typeof metadata2.activityId === "string" && isAssessmentPurpose(metadata2.purpose) && typeof metadata2.courseId === "string" && typeof metadata2.integrationId === "string" && typeof metadata2.enrollmentId === "string" && Boolean(selectedTest) && typeof selectedTest?.identifier === "string" && typeof selectedTest.contentRevision === "string" && Number.isInteger(metadata2.attemptNumber) && Number.isInteger(metadata2.responseVersion) && Boolean(metadata2.responses) && typeof metadata2.responses === "object" && Array.isArray(metadata2.itemSubmissions) && metadata2.itemSubmissions.every(isAssessmentItemSubmission) && typeof metadata2.startedAt === "string" && typeof metadata2.updatedAt === "string" && (metadata2.purpose === "mastery" ? isMasteryAttemptMetadata(mastery) : mastery === undefined) && (metadata2.purpose === "review" ? isReviewAttemptMetadata(review) : review === undefined) && (metadata2.submissionId === undefined || typeof metadata2.submissionId === "string") && (metadata2.score === undefined || isAssessmentScore(metadata2.score)) && (metadata2.completion === undefined || isRecord(metadata2.completion) && typeof metadata2.completion.testName === "string" && Number.isInteger(metadata2.completion.correctQuestions) && (metadata2.completion.totalQuestions === undefined || Number.isInteger(metadata2.completion.totalQuestions) && metadata2.completion.totalQuestions >= 0) && (metadata2.completion.itemOutcomes === undefined || metadata2.purpose === "review" && Array.isArray(metadata2.completion.itemOutcomes) && metadata2.completion.itemOutcomes.every(isReviewItemOutcome)));
10349
+ return metadata2.version === 1 && typeof metadata2.activityId === "string" && isAssessmentPurpose(metadata2.purpose) && typeof metadata2.courseId === "string" && typeof metadata2.integrationId === "string" && typeof metadata2.enrollmentId === "string" && Boolean(selectedTest) && typeof selectedTest?.identifier === "string" && typeof selectedTest.contentRevision === "string" && Number.isInteger(metadata2.attemptNumber) && Number.isInteger(metadata2.responseVersion) && Boolean(metadata2.responses) && typeof metadata2.responses === "object" && Array.isArray(metadata2.itemSubmissions) && metadata2.itemSubmissions.every(isAssessmentItemSubmission) && typeof metadata2.startedAt === "string" && typeof metadata2.updatedAt === "string" && (metadata2.purpose === "mastery" ? isMasteryAttemptMetadata(mastery) : mastery === undefined) && (metadata2.purpose === "review" ? isReviewAttemptMetadata(review) : review === undefined) && (metadata2.submissionId === undefined || typeof metadata2.submissionId === "string") && (metadata2.score === undefined || isAssessmentScore(metadata2.score)) && (metadata2.completion === undefined || isRecord(metadata2.completion) && typeof metadata2.completion.testName === "string" && Number.isInteger(metadata2.completion.correctQuestions) && (metadata2.completion.session === undefined || isAssessmentSessionTiming(metadata2.completion.session)) && (metadata2.completion.totalQuestions === undefined || Number.isInteger(metadata2.completion.totalQuestions) && metadata2.completion.totalQuestions >= 0) && (metadata2.completion.itemOutcomes === undefined || metadata2.purpose === "review" && Array.isArray(metadata2.completion.itemOutcomes) && metadata2.completion.itemOutcomes.every(isReviewItemOutcome)));
10341
10350
  }
10342
10351
  function isPlaycademyAssessmentItemResultMetadataV1(value) {
10343
10352
  if (!isRecord(value) || !isRecord(value.responses)) {
@@ -10394,6 +10403,7 @@ var init_assessment_runtime = __esm(() => {
10394
10403
  init_src();
10395
10404
  init_timeback3();
10396
10405
  init_src();
10406
+ init_uuid();
10397
10407
  init_src();
10398
10408
  init_uuid();
10399
10409
  init_src();
@@ -10652,7 +10662,10 @@ var init_assessment_runtime = __esm(() => {
10652
10662
  });
10653
10663
  SubmitAssessmentBodySchema = exports_external.object({
10654
10664
  expectedResponseVersion: exports_external.number().int().nonnegative(),
10655
- submissionId: exports_external.string().trim().min(1)
10665
+ submissionId: exports_external.string().trim().min(1),
10666
+ session: exports_external.custom(isAssessmentSessionTiming, {
10667
+ message: "Session timing needs a UUID runId and resumeId with non-negative active and inactive seconds"
10668
+ }).optional()
10656
10669
  });
10657
10670
  StartRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.and(StartAssessmentBodySchema);
10658
10671
  SaveRuntimeAssessmentRequestSchema = AssessmentRuntimeIdentitySchema.extend(SaveAssessmentBodySchema.shape);
@@ -19108,10 +19121,10 @@ var require_util = __commonJS((exports) => {
19108
19121
  return objectToString(re) === "[object RegExp]";
19109
19122
  }
19110
19123
  exports.isRegExp = isRegExp;
19111
- function isObject(arg) {
19124
+ function isObject2(arg) {
19112
19125
  return typeof arg === "object" && arg !== null;
19113
19126
  }
19114
- exports.isObject = isObject;
19127
+ exports.isObject = isObject2;
19115
19128
  function isDate(d) {
19116
19129
  return objectToString(d) === "[object Date]";
19117
19130
  }
@@ -35461,23 +35474,23 @@ var init_game_member_service = __esm(() => {
35461
35474
  });
35462
35475
 
35463
35476
  // ../timeback/dist/types.js
35464
- function isObject(value) {
35477
+ function isObject2(value) {
35465
35478
  return typeof value === "object" && value !== null;
35466
35479
  }
35467
35480
  function isCourseMetadata(value) {
35468
- return isObject(value);
35481
+ return isObject2(value);
35469
35482
  }
35470
35483
  function isResourceMetadata(value) {
35471
- return isObject(value);
35484
+ return isObject2(value);
35472
35485
  }
35473
35486
  function isPlaycademyResourceMetadata(value) {
35474
- if (!isObject(value)) {
35487
+ if (!isObject2(value)) {
35475
35488
  return false;
35476
35489
  }
35477
35490
  if (!("mastery" in value) || value.mastery === undefined) {
35478
35491
  return true;
35479
35492
  }
35480
- return isObject(value.mastery);
35493
+ return isObject2(value.mastery);
35481
35494
  }
35482
35495
  function isTimebackSubject(value) {
35483
35496
  return typeof value === "string" && SUBJECT_VALUES.includes(value);
@@ -35488,6 +35501,7 @@ function isTimebackGrade2(value) {
35488
35501
  var SUBJECT_VALUES, GRADE_VALUES2;
35489
35502
  var init_types2 = __esm(() => {
35490
35503
  init_src();
35504
+ init_uuid();
35491
35505
  SUBJECT_VALUES = TIMEBACK_SUBJECTS;
35492
35506
  GRADE_VALUES2 = TIMEBACK_GRADES;
35493
35507
  });
@@ -39899,7 +39913,7 @@ __export(exports_chunk_wex8rmr0, {
39899
39913
  isString: () => isString,
39900
39914
  isRegExp: () => isRegExp,
39901
39915
  isPrimitive: () => isPrimitive,
39902
- isObject: () => isObject2,
39916
+ isObject: () => isObject3,
39903
39917
  isNumber: () => isNumber,
39904
39918
  isNullOrUndefined: () => isNullOrUndefined,
39905
39919
  isNull: () => isNull2,
@@ -39949,7 +39963,7 @@ function format(f, ...args2) {
39949
39963
  }
39950
39964
  });
39951
39965
  for (var x = args2[i2];i2 < len; x = args2[++i2])
39952
- if (isNull2(x) || !isObject2(x))
39966
+ if (isNull2(x) || !isObject3(x))
39953
39967
  str += " " + x;
39954
39968
  else
39955
39969
  str += " " + inspect(x);
@@ -40157,16 +40171,16 @@ function isUndefined(arg) {
40157
40171
  return arg === undefined;
40158
40172
  }
40159
40173
  function isRegExp(re) {
40160
- return isObject2(re) && objectToString(re) === "[object RegExp]";
40174
+ return isObject3(re) && objectToString(re) === "[object RegExp]";
40161
40175
  }
40162
- function isObject2(arg) {
40176
+ function isObject3(arg) {
40163
40177
  return typeof arg === "object" && arg !== null;
40164
40178
  }
40165
40179
  function isDate(d) {
40166
- return isObject2(d) && objectToString(d) === "[object Date]";
40180
+ return isObject3(d) && objectToString(d) === "[object Date]";
40167
40181
  }
40168
40182
  function isError(e) {
40169
- return isObject2(e) && (objectToString(e) === "[object Error]" || e instanceof Error);
40183
+ return isObject3(e) && (objectToString(e) === "[object Error]" || e instanceof Error);
40170
40184
  }
40171
40185
  function isFunction(arg) {
40172
40186
  return typeof arg === "function";
@@ -40195,7 +40209,7 @@ function inherits(ctor, superCtor) {
40195
40209
  ctor.super_ = superCtor, ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } });
40196
40210
  }
40197
40211
  function _extend(origin, add) {
40198
- if (!add || !isObject2(add))
40212
+ if (!add || !isObject3(add))
40199
40213
  return origin;
40200
40214
  var keys = Object.keys(add), i2 = keys.length;
40201
40215
  while (i2--)
@@ -42012,7 +42026,7 @@ __export(exports_util, {
42012
42026
  joinValues: () => joinValues,
42013
42027
  issue: () => issue2,
42014
42028
  isPlainObject: () => isPlainObject,
42015
- isObject: () => isObject3,
42029
+ isObject: () => isObject4,
42016
42030
  getSizableOrigin: () => getSizableOrigin,
42017
42031
  getParsedType: () => getParsedType2,
42018
42032
  getLengthableOrigin: () => getLengthableOrigin,
@@ -42148,17 +42162,17 @@ function randomString(length = 10) {
42148
42162
  function esc(str) {
42149
42163
  return JSON.stringify(str);
42150
42164
  }
42151
- function isObject3(data) {
42165
+ function isObject4(data) {
42152
42166
  return typeof data === "object" && data !== null && !Array.isArray(data);
42153
42167
  }
42154
42168
  function isPlainObject(o) {
42155
- if (isObject3(o) === false)
42169
+ if (isObject4(o) === false)
42156
42170
  return false;
42157
42171
  const ctor = o.constructor;
42158
42172
  if (ctor === undefined)
42159
42173
  return true;
42160
42174
  const prot = ctor.prototype;
42161
- if (isObject3(prot) === false)
42175
+ if (isObject4(prot) === false)
42162
42176
  return false;
42163
42177
  if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) {
42164
42178
  return false;
@@ -44265,7 +44279,7 @@ var init_schemas5 = __esm(() => {
44265
44279
  return (payload, ctx) => fn(shape, payload, ctx);
44266
44280
  };
44267
44281
  let fastpass;
44268
- const isObject4 = isObject3;
44282
+ const isObject5 = isObject4;
44269
44283
  const jit = !globalConfig.jitless;
44270
44284
  const allowsEval2 = allowsEval;
44271
44285
  const fastEnabled = jit && allowsEval2.value;
@@ -44274,7 +44288,7 @@ var init_schemas5 = __esm(() => {
44274
44288
  inst._zod.parse = (payload, ctx) => {
44275
44289
  value ?? (value = _normalized.value);
44276
44290
  const input = payload.value;
44277
- if (!isObject4(input)) {
44291
+ if (!isObject5(input)) {
44278
44292
  payload.issues.push({
44279
44293
  expected: "object",
44280
44294
  code: "invalid_type",
@@ -44418,7 +44432,7 @@ var init_schemas5 = __esm(() => {
44418
44432
  });
44419
44433
  inst._zod.parse = (payload, ctx) => {
44420
44434
  const input = payload.value;
44421
- if (!isObject3(input)) {
44435
+ if (!isObject4(input)) {
44422
44436
  payload.issues.push({
44423
44437
  code: "invalid_type",
44424
44438
  expected: "object",
@@ -59379,7 +59393,7 @@ __export(exports_chunk_dd0pbbq3, {
59379
59393
  isString: () => isString2,
59380
59394
  isRegExp: () => isRegExp2,
59381
59395
  isPrimitive: () => isPrimitive2,
59382
- isObject: () => isObject4,
59396
+ isObject: () => isObject5,
59383
59397
  isNumber: () => isNumber2,
59384
59398
  isNullOrUndefined: () => isNullOrUndefined2,
59385
59399
  isNull: () => isNull3,
@@ -59429,7 +59443,7 @@ function format2(f, ...args2) {
59429
59443
  }
59430
59444
  });
59431
59445
  for (var x = args2[i2];i2 < len; x = args2[++i2])
59432
- if (isNull3(x) || !isObject4(x))
59446
+ if (isNull3(x) || !isObject5(x))
59433
59447
  str += " " + x;
59434
59448
  else
59435
59449
  str += " " + inspect2(x);
@@ -59637,16 +59651,16 @@ function isUndefined2(arg) {
59637
59651
  return arg === undefined;
59638
59652
  }
59639
59653
  function isRegExp2(re) {
59640
- return isObject4(re) && objectToString2(re) === "[object RegExp]";
59654
+ return isObject5(re) && objectToString2(re) === "[object RegExp]";
59641
59655
  }
59642
- function isObject4(arg) {
59656
+ function isObject5(arg) {
59643
59657
  return typeof arg === "object" && arg !== null;
59644
59658
  }
59645
59659
  function isDate2(d) {
59646
- return isObject4(d) && objectToString2(d) === "[object Date]";
59660
+ return isObject5(d) && objectToString2(d) === "[object Date]";
59647
59661
  }
59648
59662
  function isError2(e) {
59649
- return isObject4(e) && (objectToString2(e) === "[object Error]" || e instanceof Error);
59663
+ return isObject5(e) && (objectToString2(e) === "[object Error]" || e instanceof Error);
59650
59664
  }
59651
59665
  function isFunction2(arg) {
59652
59666
  return typeof arg === "function";
@@ -59675,7 +59689,7 @@ function inherits2(ctor, superCtor) {
59675
59689
  ctor.super_ = superCtor, ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } });
59676
59690
  }
59677
59691
  function _extend2(origin, add) {
59678
- if (!add || !isObject4(add))
59692
+ if (!add || !isObject5(add))
59679
59693
  return origin;
59680
59694
  var keys = Object.keys(add), i2 = keys.length;
59681
59695
  while (i2--)
@@ -61457,7 +61471,7 @@ __export(exports_util2, {
61457
61471
  joinValues: () => joinValues2,
61458
61472
  issue: () => issue4,
61459
61473
  isPlainObject: () => isPlainObject2,
61460
- isObject: () => isObject5,
61474
+ isObject: () => isObject6,
61461
61475
  hexToUint8Array: () => hexToUint8Array,
61462
61476
  getSizableOrigin: () => getSizableOrigin2,
61463
61477
  getParsedType: () => getParsedType3,
@@ -61618,11 +61632,11 @@ function esc2(str) {
61618
61632
  function slugify(input) {
61619
61633
  return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
61620
61634
  }
61621
- function isObject5(data) {
61635
+ function isObject6(data) {
61622
61636
  return typeof data === "object" && data !== null && !Array.isArray(data);
61623
61637
  }
61624
61638
  function isPlainObject2(o) {
61625
- if (isObject5(o) === false)
61639
+ if (isObject6(o) === false)
61626
61640
  return false;
61627
61641
  const ctor = o.constructor;
61628
61642
  if (ctor === undefined)
@@ -61630,7 +61644,7 @@ function isPlainObject2(o) {
61630
61644
  if (typeof ctor !== "function")
61631
61645
  return true;
61632
61646
  const prot = ctor.prototype;
61633
- if (isObject5(prot) === false)
61647
+ if (isObject6(prot) === false)
61634
61648
  return false;
61635
61649
  if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) {
61636
61650
  return false;
@@ -64148,13 +64162,13 @@ var init_schemas7 = __esm(() => {
64148
64162
  }
64149
64163
  return propValues;
64150
64164
  });
64151
- const isObject6 = isObject5;
64165
+ const isObject7 = isObject6;
64152
64166
  const catchall = def.catchall;
64153
64167
  let value;
64154
64168
  inst._zod.parse = (payload, ctx) => {
64155
64169
  value ?? (value = _normalized.value);
64156
64170
  const input = payload.value;
64157
- if (!isObject6(input)) {
64171
+ if (!isObject7(input)) {
64158
64172
  payload.issues.push({
64159
64173
  expected: "object",
64160
64174
  code: "invalid_type",
@@ -64281,7 +64295,7 @@ var init_schemas7 = __esm(() => {
64281
64295
  return (payload, ctx) => fn(shape, payload, ctx);
64282
64296
  };
64283
64297
  let fastpass;
64284
- const isObject6 = isObject5;
64298
+ const isObject7 = isObject6;
64285
64299
  const jit = !globalConfig2.jitless;
64286
64300
  const allowsEval3 = allowsEval2;
64287
64301
  const fastEnabled = jit && allowsEval3.value;
@@ -64290,7 +64304,7 @@ var init_schemas7 = __esm(() => {
64290
64304
  inst._zod.parse = (payload, ctx) => {
64291
64305
  value ?? (value = _normalized.value);
64292
64306
  const input = payload.value;
64293
- if (!isObject6(input)) {
64307
+ if (!isObject7(input)) {
64294
64308
  payload.issues.push({
64295
64309
  expected: "object",
64296
64310
  code: "invalid_type",
@@ -64422,7 +64436,7 @@ var init_schemas7 = __esm(() => {
64422
64436
  });
64423
64437
  inst._zod.parse = (payload, ctx) => {
64424
64438
  const input = payload.value;
64425
- if (!isObject5(input)) {
64439
+ if (!isObject6(input)) {
64426
64440
  payload.issues.push({
64427
64441
  code: "invalid_type",
64428
64442
  expected: "object",
@@ -78476,7 +78490,7 @@ __export(exports_chunk_dd0pbbq32, {
78476
78490
  isString: () => isString3,
78477
78491
  isRegExp: () => isRegExp3,
78478
78492
  isPrimitive: () => isPrimitive3,
78479
- isObject: () => isObject6,
78493
+ isObject: () => isObject7,
78480
78494
  isNumber: () => isNumber3,
78481
78495
  isNullOrUndefined: () => isNullOrUndefined3,
78482
78496
  isNull: () => isNull4,
@@ -78526,7 +78540,7 @@ function format3(f, ...args2) {
78526
78540
  }
78527
78541
  });
78528
78542
  for (var x = args2[i2];i2 < len; x = args2[++i2])
78529
- if (isNull4(x) || !isObject6(x))
78543
+ if (isNull4(x) || !isObject7(x))
78530
78544
  str += " " + x;
78531
78545
  else
78532
78546
  str += " " + inspect3(x);
@@ -78734,16 +78748,16 @@ function isUndefined3(arg) {
78734
78748
  return arg === undefined;
78735
78749
  }
78736
78750
  function isRegExp3(re) {
78737
- return isObject6(re) && objectToString3(re) === "[object RegExp]";
78751
+ return isObject7(re) && objectToString3(re) === "[object RegExp]";
78738
78752
  }
78739
- function isObject6(arg) {
78753
+ function isObject7(arg) {
78740
78754
  return typeof arg === "object" && arg !== null;
78741
78755
  }
78742
78756
  function isDate3(d) {
78743
- return isObject6(d) && objectToString3(d) === "[object Date]";
78757
+ return isObject7(d) && objectToString3(d) === "[object Date]";
78744
78758
  }
78745
78759
  function isError3(e) {
78746
- return isObject6(e) && (objectToString3(e) === "[object Error]" || e instanceof Error);
78760
+ return isObject7(e) && (objectToString3(e) === "[object Error]" || e instanceof Error);
78747
78761
  }
78748
78762
  function isFunction3(arg) {
78749
78763
  return typeof arg === "function";
@@ -78772,7 +78786,7 @@ function inherits3(ctor, superCtor) {
78772
78786
  ctor.super_ = superCtor, ctor.prototype = Object.create(superCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } });
78773
78787
  }
78774
78788
  function _extend3(origin, add) {
78775
- if (!add || !isObject6(add))
78789
+ if (!add || !isObject7(add))
78776
78790
  return origin;
78777
78791
  var keys = Object.keys(add), i2 = keys.length;
78778
78792
  while (i2--)
@@ -83278,7 +83292,8 @@ function buildAssessmentCompletionMetadata(input) {
83278
83292
  attemptId: input.attemptId,
83279
83293
  submissionId: input.submissionId,
83280
83294
  testIdentifier: input.testIdentifier,
83281
- assessmentPurpose: input.purpose
83295
+ assessmentPurpose: input.purpose,
83296
+ ...input.resumeId ? { resumeId: input.resumeId } : {}
83282
83297
  }
83283
83298
  };
83284
83299
  }
@@ -83300,11 +83315,13 @@ function buildAssessmentCompletionEvent(input) {
83300
83315
  appName: input.appName,
83301
83316
  sensorUrl: input.sensorUrl,
83302
83317
  eventId: input.eventId,
83318
+ runId: input.attemptId,
83303
83319
  generatedExtensions: metadata2,
83304
83320
  eventExtensions: {
83305
83321
  playcademy: {
83306
83322
  assessmentPurpose: input.purpose,
83307
- attemptId: input.attemptId
83323
+ attemptId: input.attemptId,
83324
+ ...input.resumeId ? { resumeId: input.resumeId } : {}
83308
83325
  }
83309
83326
  }
83310
83327
  };
@@ -83922,17 +83939,17 @@ function createActivityNamespace(core3, deps) {
83922
83939
  completeAssessment: (data) => deps.events.emitActivityEvent(buildAssessmentCompletionEvent(data))
83923
83940
  };
83924
83941
  }
83925
- function isObject7(value) {
83942
+ function isObject8(value) {
83926
83943
  return typeof value === "object" && value !== null;
83927
83944
  }
83928
83945
  function isPlaycademyResourceMetadata2(value) {
83929
- if (!isObject7(value)) {
83946
+ if (!isObject8(value)) {
83930
83947
  return false;
83931
83948
  }
83932
83949
  if (!("mastery" in value) || value.mastery === undefined) {
83933
83950
  return true;
83934
83951
  }
83935
- return isObject7(value.mastery);
83952
+ return isObject8(value.mastery);
83936
83953
  }
83937
83954
  function isTimebackSubject3(value) {
83938
83955
  return typeof value === "string" && SUBJECT_VALUES2.includes(value);
@@ -85205,6 +85222,7 @@ var init_dist5 = __esm(async () => {
85205
85222
  init_src();
85206
85223
  init_spans();
85207
85224
  init_src();
85225
+ init_uuid();
85208
85226
  init_src();
85209
85227
  init_spans();
85210
85228
  init_src9();
@@ -85420,6 +85438,7 @@ function deriveSourcedIds2(courseId) {
85420
85438
  }
85421
85439
  var init_utils6 = __esm(() => {
85422
85440
  init_src();
85441
+ init_uuid();
85423
85442
  });
85424
85443
 
85425
85444
  // ../api-core/src/utils/timeback-admin-metrics.util.ts
@@ -91389,6 +91408,7 @@ function buildAssessmentResultFinalization(input) {
91389
91408
  testName: input.testName,
91390
91409
  totalQuestions: input.totalQuestions,
91391
91410
  correctQuestions: input.correctQuestions,
91411
+ ...input.session ? { session: input.session } : {},
91392
91412
  ...input.itemOutcomes ? { itemOutcomes: input.itemOutcomes } : {}
91393
91413
  },
91394
91414
  updatedAt: input.timestamp
@@ -91497,7 +91517,7 @@ class TimebackAssessmentRuntimeService {
91497
91517
  const compatibleAttempts = this.attemptsMatchingRequest(attempts, requestFingerprint);
91498
91518
  let decision = this.requireDecision(selectRuntimeAssessment(tests, [...compatibleAttempts.values()].map((entry) => entry.selection)), input.purpose);
91499
91519
  if (decision.kind === "resume") {
91500
- return this.resumeSnapshot(decision, attempts);
91520
+ return this.resumeSnapshot(decision, attempts, context2.integration);
91501
91521
  }
91502
91522
  let assessment = await this.loadAssessment(decision.test.qtiTestIdentifier);
91503
91523
  const refreshedListing = await this.listAttempts(attemptScope);
@@ -91505,7 +91525,7 @@ class TimebackAssessmentRuntimeService {
91505
91525
  const refreshedCompatible = this.attemptsMatchingRequest(refreshed, requestFingerprint);
91506
91526
  const refreshedDecision = this.requireDecision(selectRuntimeAssessment(tests, [...refreshedCompatible.values()].map((entry) => entry.selection)), input.purpose);
91507
91527
  if (refreshedDecision.kind === "resume") {
91508
- return this.resumeSnapshot(refreshedDecision, refreshed);
91528
+ return this.resumeSnapshot(refreshedDecision, refreshed, context2.integration);
91509
91529
  }
91510
91530
  decision = refreshedDecision;
91511
91531
  if (assessment.identifier !== decision.test.qtiTestIdentifier) {
@@ -91565,7 +91585,7 @@ class TimebackAssessmentRuntimeService {
91565
91585
  grade: context2.integration.grade
91566
91586
  })
91567
91587
  });
91568
- return this.snapshot(result, metadata2, assessment);
91588
+ return this.snapshot(result, metadata2, assessment, context2.integration);
91569
91589
  });
91570
91590
  }
91571
91591
  async startReview({
@@ -91700,12 +91720,12 @@ class TimebackAssessmentRuntimeService {
91700
91720
  existingChildResults: refreshedListing.reviewChildren,
91701
91721
  lookupMissingResults: false
91702
91722
  });
91703
- return this.snapshot(result, metadata2, assessment);
91723
+ return this.snapshot(result, metadata2, assessment, context2.integration);
91704
91724
  });
91705
91725
  }
91706
91726
  async get(params) {
91707
91727
  const attempt = await this.authorizeAttemptRead(params);
91708
- return this.snapshotForResult(attempt.result, attempt.metadata);
91728
+ return this.snapshotForResult(attempt.result, attempt.metadata, attempt.integration);
91709
91729
  }
91710
91730
  async latest({
91711
91731
  gameId,
@@ -92054,6 +92074,9 @@ class TimebackAssessmentRuntimeService {
92054
92074
  if (submissionDisposition === "reject") {
92055
92075
  throw AssessmentRuntimeError.from(assessmentAlreadySubmitted(attempt.result.sourcedId));
92056
92076
  }
92077
+ if (input.session && input.session.runId !== attempt.result.sourcedId) {
92078
+ throw new ValidationError("Assessment session runId must match the attemptId.");
92079
+ }
92057
92080
  this.assertResponseVersion(attempt.metadata, input.expectedResponseVersion);
92058
92081
  if (assessmentFlowForPurpose(attempt.metadata.purpose) === "item-submit") {
92059
92082
  const assessment2 = await this.loadAttemptAssessment(attempt.metadata);
@@ -92082,6 +92105,7 @@ class TimebackAssessmentRuntimeService {
92082
92105
  testName: assessment.title,
92083
92106
  totalQuestions: assessment.items.length,
92084
92107
  correctQuestions,
92108
+ ...input.session ? { session: input.session } : {},
92085
92109
  ...reviewOutcomes ? { itemOutcomes: reviewOutcomes } : {}
92086
92110
  });
92087
92111
  const metadata2 = finalization.metadata;
@@ -92120,7 +92144,10 @@ class TimebackAssessmentRuntimeService {
92120
92144
  };
92121
92145
  });
92122
92146
  if (submission.completion) {
92123
- await this.emitCompletionBestEffort(submission.completion);
92147
+ await Promise.allSettled([
92148
+ this.emitCompletionBestEffort(submission.completion),
92149
+ this.emitSessionBestEffort(submission.completion)
92150
+ ]);
92124
92151
  }
92125
92152
  return submission.response;
92126
92153
  }
@@ -92375,7 +92402,7 @@ class TimebackAssessmentRuntimeService {
92375
92402
  }
92376
92403
  return decision;
92377
92404
  }
92378
- resumeSnapshot(decision, attempts) {
92405
+ resumeSnapshot(decision, attempts, integration) {
92379
92406
  if (decision.additionalResumableAttemptIds.length > 0) {
92380
92407
  addEvent("assessment.multiple_resumable_attempts", {
92381
92408
  "app.assessment.attempt_id": decision.attempt.attemptId,
@@ -92383,7 +92410,7 @@ class TimebackAssessmentRuntimeService {
92383
92410
  });
92384
92411
  }
92385
92412
  const resumed = attempts.get(decision.attempt.attemptId);
92386
- return this.snapshotForResult(resumed.result, resumed.metadata);
92413
+ return this.snapshotForResult(resumed.result, resumed.metadata, integration);
92387
92414
  }
92388
92415
  async resumeReviewSnapshot(decision, attempts, existingChildResults, context2) {
92389
92416
  if (decision.additionalResumableAttemptIds.length > 0) {
@@ -92408,7 +92435,7 @@ class TimebackAssessmentRuntimeService {
92408
92435
  existingChildResults,
92409
92436
  lookupMissingResults: true
92410
92437
  });
92411
- return this.snapshot(resumed.result, resumed.metadata, assessment);
92438
+ return this.snapshot(resumed.result, resumed.metadata, assessment, context2.integration);
92412
92439
  }
92413
92440
  async loadReviewBank(identifier, expectedSourceRevision) {
92414
92441
  if (expectedSourceRevision) {
@@ -92873,17 +92900,19 @@ class TimebackAssessmentRuntimeService {
92873
92900
  unauthorizedAttempt(attemptId) {
92874
92901
  return AssessmentRuntimeError.from(assessmentAttemptUnauthorized(attemptId));
92875
92902
  }
92876
- async snapshotForResult(result, metadata2) {
92877
- return this.snapshot(result, metadata2, await this.loadAttemptAssessment(metadata2));
92903
+ async snapshotForResult(result, metadata2, integration) {
92904
+ return this.snapshot(result, metadata2, await this.loadAttemptAssessment(metadata2), integration);
92878
92905
  }
92879
- snapshot(result, metadata2, assessment) {
92906
+ snapshot(result, metadata2, assessment, integration) {
92880
92907
  const completed = this.isCompleted(result);
92881
92908
  const selection = this.selectionContext(metadata2);
92909
+ const activityData = this.activityData(metadata2, assessment, integration);
92882
92910
  return {
92883
92911
  attemptId: result.sourcedId,
92884
92912
  responseVersion: metadata2.responseVersion,
92885
92913
  status: completed ? "completed" : "in_progress",
92886
92914
  flow: assessmentFlowForPurpose(metadata2.purpose),
92915
+ ...activityData ? { activityData } : {},
92887
92916
  assessment: assessmentPresentationForAttempt(assessment, result.sourcedId),
92888
92917
  responses: metadata2.responses,
92889
92918
  itemSubmissions: metadata2.itemSubmissions,
@@ -92891,6 +92920,30 @@ class TimebackAssessmentRuntimeService {
92891
92920
  selection
92892
92921
  };
92893
92922
  }
92923
+ activityData(metadata2, assessment, integration) {
92924
+ const course = this.trackableCourse(integration, "assessment.activity_tracking_invalid_course_metadata");
92925
+ if (!course) {
92926
+ return;
92927
+ }
92928
+ return {
92929
+ activityId: metadata2.activityId,
92930
+ activityName: assessment.title,
92931
+ ...course,
92932
+ courseId: integration.courseId
92933
+ };
92934
+ }
92935
+ trackableCourse(integration, event, attributes2 = {}) {
92936
+ if (!isTimebackGrade2(integration.grade) || !isTimebackSubject(integration.subject)) {
92937
+ addEvent(event, {
92938
+ ...attributes2,
92939
+ "app.timeback.integration_id": integration.id,
92940
+ "app.timeback.grade": integration.grade,
92941
+ "app.timeback.subject": integration.subject
92942
+ });
92943
+ return null;
92944
+ }
92945
+ return { grade: integration.grade, subject: integration.subject };
92946
+ }
92894
92947
  selectionContext(metadata2) {
92895
92948
  if (metadata2.purpose === "review") {
92896
92949
  return {
@@ -93002,12 +93055,9 @@ class TimebackAssessmentRuntimeService {
93002
93055
  async emitCompletionBestEffort(input) {
93003
93056
  const { attempt } = input;
93004
93057
  const integration = attempt.integration;
93005
- if (!isTimebackGrade2(integration.grade) || !isTimebackSubject(integration.subject)) {
93006
- addEvent("assessment.completion_event_invalid_course_metadata", {
93007
- "app.assessment.attempt_id": attempt.result.sourcedId,
93008
- "app.timeback.grade": integration.grade,
93009
- "app.timeback.subject": integration.subject
93010
- });
93058
+ const session2 = attempt.metadata.completion?.session;
93059
+ const course = this.trackableCourse(integration, "assessment.completion_event_invalid_course_metadata", { "app.assessment.attempt_id": attempt.result.sourcedId });
93060
+ if (!course) {
93011
93061
  return;
93012
93062
  }
93013
93063
  try {
@@ -93029,14 +93079,14 @@ class TimebackAssessmentRuntimeService {
93029
93079
  testName: input.testName,
93030
93080
  courseId: integration.courseId,
93031
93081
  courseName: attempt.enrollment.course.title,
93032
- subject: integration.subject,
93033
- grade: integration.grade,
93082
+ ...course,
93034
93083
  appName: input.game.appName,
93035
93084
  sensorUrl: input.game.sensorUrl,
93036
93085
  attemptNumber: attempt.metadata.attemptNumber,
93037
93086
  totalQuestions: input.totalQuestions,
93038
93087
  correctQuestions: input.correctQuestions,
93039
- eventId
93088
+ eventId,
93089
+ ...session2 ? { resumeId: session2.resumeId } : {}
93040
93090
  };
93041
93091
  await this.requireClient().activity.completeAssessment(event);
93042
93092
  } catch (error88) {
@@ -93047,6 +93097,54 @@ class TimebackAssessmentRuntimeService {
93047
93097
  });
93048
93098
  }
93049
93099
  }
93100
+ async emitSessionBestEffort(input) {
93101
+ const { attempt } = input;
93102
+ const session2 = attempt.metadata.completion?.session;
93103
+ if (!session2 || session2.activeSeconds === 0 && (session2.inactiveSeconds ?? 0) === 0) {
93104
+ return;
93105
+ }
93106
+ const integration = attempt.integration;
93107
+ const course = this.trackableCourse(integration, "assessment.time_spent_event_invalid_course_metadata", { "app.assessment.attempt_id": attempt.result.sourcedId });
93108
+ if (!course) {
93109
+ return;
93110
+ }
93111
+ try {
93112
+ const eventId = `urn:uuid:${await deterministicUUID([
93113
+ "playcademy-assessment-time-spent-event-v1",
93114
+ attempt.result.sourcedId,
93115
+ input.submissionId,
93116
+ session2.resumeId
93117
+ ].join("\x00"))}`;
93118
+ await this.requireClient().activity.session(integration.courseId, input.studentId, {
93119
+ eventId,
93120
+ gameId: input.gameId,
93121
+ activityId: attempt.metadata.activityId,
93122
+ activityName: input.testName,
93123
+ courseId: integration.courseId,
93124
+ courseName: attempt.enrollment.course.title,
93125
+ activeTimeSeconds: session2.activeSeconds,
93126
+ ...session2.inactiveSeconds !== undefined ? { inactiveTimeSeconds: session2.inactiveSeconds } : {},
93127
+ subject: course.subject,
93128
+ appName: input.game.appName,
93129
+ sensorUrl: input.game.sensorUrl,
93130
+ runId: session2.runId,
93131
+ extensions: {
93132
+ playcademy: {
93133
+ resumeId: session2.resumeId,
93134
+ assessmentPurpose: attempt.metadata.purpose,
93135
+ attemptId: attempt.result.sourcedId,
93136
+ submissionId: input.submissionId
93137
+ }
93138
+ }
93139
+ });
93140
+ } catch (error88) {
93141
+ addEvent("assessment.time_spent_event_failed", {
93142
+ "app.assessment.attempt_id": attempt.result.sourcedId,
93143
+ "exception.type": errorType(error88),
93144
+ "app.error.message": errorMessage(error88)
93145
+ });
93146
+ }
93147
+ }
93050
93148
  }
93051
93149
  var init_timeback_assessment_runtime_service = __esm(async () => {
93052
93150
  init_drizzle_orm();
@@ -117992,7 +118090,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
117992
118090
  throw new TypeError(FUNC_ERROR_TEXT);
117993
118091
  }
117994
118092
  wait = toNumber2(wait) || 0;
117995
- if (isObject8(options)) {
118093
+ if (isObject9(options)) {
117996
118094
  leading = !!options.leading;
117997
118095
  maxing = "maxWait" in options;
117998
118096
  maxWait = maxing ? nativeMax(toNumber2(options.maxWait) || 0, wait) : maxWait;
@@ -118071,7 +118169,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
118071
118169
  if (typeof func2 != "function") {
118072
118170
  throw new TypeError(FUNC_ERROR_TEXT);
118073
118171
  }
118074
- if (isObject8(options)) {
118172
+ if (isObject9(options)) {
118075
118173
  leading = "leading" in options ? !!options.leading : leading;
118076
118174
  trailing = "trailing" in options ? !!options.trailing : trailing;
118077
118175
  }
@@ -118081,7 +118179,7 @@ See: https://github.com/isaacs/node-glob/issues/167`);
118081
118179
  trailing
118082
118180
  });
118083
118181
  }
118084
- function isObject8(value) {
118182
+ function isObject9(value) {
118085
118183
  var type = typeof value;
118086
118184
  return !!value && (type == "object" || type == "function");
118087
118185
  }
@@ -118098,9 +118196,9 @@ See: https://github.com/isaacs/node-glob/issues/167`);
118098
118196
  if (isSymbol4(value)) {
118099
118197
  return NAN;
118100
118198
  }
118101
- if (isObject8(value)) {
118199
+ if (isObject9(value)) {
118102
118200
  var other = typeof value.valueOf == "function" ? value.valueOf() : value;
118103
- value = isObject8(other) ? other + "" : other;
118201
+ value = isObject9(other) ? other + "" : other;
118104
118202
  }
118105
118203
  if (typeof value != "string") {
118106
118204
  return value === 0 ? value : +value;
@@ -155035,7 +155133,8 @@ var init_timeback_controller = __esm(() => {
155035
155133
  attemptId,
155036
155134
  input: {
155037
155135
  expectedResponseVersion: body2.expectedResponseVersion,
155038
- submissionId: body2.submissionId
155136
+ submissionId: body2.submissionId,
155137
+ ...body2.session ? { session: body2.session } : {}
155039
155138
  },
155040
155139
  game: {
155041
155140
  appName: body2.appName,