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