@playcademy/vite-plugin 0.2.24-beta.6 → 0.2.24-beta.8
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/index.js +486 -200
- package/dist/types/internal.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -25336,7 +25336,7 @@ var package_default;
|
|
|
25336
25336
|
var init_package = __esm(() => {
|
|
25337
25337
|
package_default = {
|
|
25338
25338
|
name: "@playcademy/sandbox",
|
|
25339
|
-
version: "0.3.17-beta.
|
|
25339
|
+
version: "0.3.17-beta.11",
|
|
25340
25340
|
description: "Local development server for Playcademy game development",
|
|
25341
25341
|
type: "module",
|
|
25342
25342
|
exports: {
|
|
@@ -29951,6 +29951,7 @@ var init_esm = __esm(() => {
|
|
|
29951
29951
|
function createMinimalConfig(overrides) {
|
|
29952
29952
|
return apiConfigSchema.parse({
|
|
29953
29953
|
stage: "local",
|
|
29954
|
+
isLocal: false,
|
|
29954
29955
|
...overrides
|
|
29955
29956
|
});
|
|
29956
29957
|
}
|
|
@@ -29978,6 +29979,7 @@ var init_schema = __esm(() => {
|
|
|
29978
29979
|
});
|
|
29979
29980
|
apiConfigSchema = exports_external.object({
|
|
29980
29981
|
stage: stageSchema,
|
|
29982
|
+
isLocal: exports_external.boolean().default(false),
|
|
29981
29983
|
baseUrl: exports_external.string().url().optional(),
|
|
29982
29984
|
gameDomain: exports_external.string().optional(),
|
|
29983
29985
|
lti: ltiConfigSchema.optional(),
|
|
@@ -54196,6 +54198,36 @@ var init_pure = __esm(() => {
|
|
|
54196
54198
|
var init_src4 = __esm(() => {
|
|
54197
54199
|
init_pure();
|
|
54198
54200
|
});
|
|
54201
|
+
function toAttributionEventTime(date3) {
|
|
54202
|
+
if (!date3) {
|
|
54203
|
+
return;
|
|
54204
|
+
}
|
|
54205
|
+
const match = date3.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
54206
|
+
if (!match) {
|
|
54207
|
+
throw new ValidationError("Date must be in YYYY-MM-DD format");
|
|
54208
|
+
}
|
|
54209
|
+
const [, yearStr, monthStr, dayStr] = match;
|
|
54210
|
+
const year = Number(yearStr);
|
|
54211
|
+
const month = Number(monthStr);
|
|
54212
|
+
const day = Number(dayStr);
|
|
54213
|
+
if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) {
|
|
54214
|
+
throw new ValidationError("Date must be in YYYY-MM-DD format");
|
|
54215
|
+
}
|
|
54216
|
+
const eventTime = new Date(Date.UTC(year, month - 1, day, 12, 0, 0));
|
|
54217
|
+
if (eventTime.getUTCFullYear() !== year || eventTime.getUTCMonth() + 1 !== month || eventTime.getUTCDate() !== day) {
|
|
54218
|
+
throw new ValidationError("Date must be a valid calendar date");
|
|
54219
|
+
}
|
|
54220
|
+
return eventTime.toISOString();
|
|
54221
|
+
}
|
|
54222
|
+
function resolveAdminEventTime(data) {
|
|
54223
|
+
if (data.useCurrentTime) {
|
|
54224
|
+
return new Date().toISOString();
|
|
54225
|
+
}
|
|
54226
|
+
return toAttributionEventTime(data.date);
|
|
54227
|
+
}
|
|
54228
|
+
var init_timeback_admin_util = __esm(() => {
|
|
54229
|
+
init_errors();
|
|
54230
|
+
});
|
|
54199
54231
|
function isRecord2(value) {
|
|
54200
54232
|
return typeof value === "object" && value !== null;
|
|
54201
54233
|
}
|
|
@@ -54240,14 +54272,6 @@ function getPlaycademyMetadata(event) {
|
|
|
54240
54272
|
const extensions = getMergedCaliperExtensions(event);
|
|
54241
54273
|
return isRecord2(extensions.playcademy) ? extensions.playcademy : undefined;
|
|
54242
54274
|
}
|
|
54243
|
-
function getAssessmentPlaycademyMetadata(assessment) {
|
|
54244
|
-
return isRecord2(assessment.metadata?.playcademy) ? assessment.metadata.playcademy : undefined;
|
|
54245
|
-
}
|
|
54246
|
-
function isRemediationAssessmentResult(assessment) {
|
|
54247
|
-
const playcademy = getAssessmentPlaycademyMetadata(assessment);
|
|
54248
|
-
const eventKind = getStringValue(playcademy?.eventKind);
|
|
54249
|
-
return eventKind === "remediation-xp" || eventKind === "remediation-time" || eventKind === "remediation-mastery";
|
|
54250
|
-
}
|
|
54251
54275
|
function getActivityId(event, playcademy) {
|
|
54252
54276
|
const metadataActivityId = getStringValue(playcademy?.activityId);
|
|
54253
54277
|
if (metadataActivityId) {
|
|
@@ -54264,8 +54288,8 @@ function getActivityId(event, playcademy) {
|
|
|
54264
54288
|
const trimmed = objectId.replace(/\/$/, "");
|
|
54265
54289
|
const segments = trimmed.split("/");
|
|
54266
54290
|
const activityIndex = segments.lastIndexOf("activities");
|
|
54267
|
-
if (activityIndex !== -1 && segments.length >= activityIndex +
|
|
54268
|
-
const candidate = segments[activityIndex +
|
|
54291
|
+
if (activityIndex !== -1 && segments.length >= activityIndex + 3) {
|
|
54292
|
+
const candidate = segments[activityIndex + 2];
|
|
54269
54293
|
return candidate ? decodeURIComponent(candidate) : undefined;
|
|
54270
54294
|
}
|
|
54271
54295
|
return;
|
|
@@ -54318,38 +54342,96 @@ function mapAssessmentsToXpEvents(userId, assessments) {
|
|
|
54318
54342
|
};
|
|
54319
54343
|
});
|
|
54320
54344
|
}
|
|
54321
|
-
function
|
|
54322
|
-
|
|
54345
|
+
function getDurationSecondsFromExtensions(event) {
|
|
54346
|
+
const extensions = getMergedCaliperExtensions(event);
|
|
54347
|
+
const playcademy = isRecord2(extensions.playcademy) ? extensions.playcademy : undefined;
|
|
54348
|
+
const rawValue = extensions.durationSeconds ?? playcademy?.durationSeconds;
|
|
54349
|
+
const value = typeof rawValue === "number" ? rawValue : Number(rawValue);
|
|
54350
|
+
return Number.isFinite(value) ? value : undefined;
|
|
54351
|
+
}
|
|
54352
|
+
function getCanonicalRunId(session2) {
|
|
54353
|
+
const sessionId = getStringValue(session2?.id);
|
|
54354
|
+
if (!sessionId) {
|
|
54355
|
+
return;
|
|
54356
|
+
}
|
|
54357
|
+
return sessionId.replace(/^urn:uuid:/, "");
|
|
54358
|
+
}
|
|
54359
|
+
function getResumeId(event) {
|
|
54360
|
+
const playcademy = getPlaycademyMetadata(event);
|
|
54361
|
+
return getStringValue(playcademy?.resumeId);
|
|
54362
|
+
}
|
|
54363
|
+
function isCaliperRemediationOrCompletionEvent(event) {
|
|
54364
|
+
const playcademy = getPlaycademyMetadata(event);
|
|
54365
|
+
return REMEDIATION_OR_COMPLETION_EVENT_KINDS.has(getStringValue(playcademy?.eventKind) || "");
|
|
54366
|
+
}
|
|
54367
|
+
function groupCaliperEventsByRun(events) {
|
|
54368
|
+
const groups = new Map;
|
|
54369
|
+
for (const event of events) {
|
|
54370
|
+
const objectId = getStringValue(event.object.id) || "unknown-activity";
|
|
54371
|
+
const groupKey = `${objectId}::${getStringValue(event.session?.id) || event.externalId}`;
|
|
54372
|
+
const existing = groups.get(groupKey);
|
|
54373
|
+
if (existing) {
|
|
54374
|
+
existing.push(event);
|
|
54375
|
+
} else {
|
|
54376
|
+
groups.set(groupKey, [event]);
|
|
54377
|
+
}
|
|
54378
|
+
}
|
|
54379
|
+
return groups;
|
|
54323
54380
|
}
|
|
54324
|
-
function
|
|
54325
|
-
if (
|
|
54381
|
+
function mapCaliperEventGroupToActivity(events, relevantCourseIds) {
|
|
54382
|
+
if (events.length === 0) {
|
|
54326
54383
|
return null;
|
|
54327
54384
|
}
|
|
54328
|
-
|
|
54385
|
+
const sortedEvents = events.toSorted((a, b) => a.eventTime.localeCompare(b.eventTime));
|
|
54386
|
+
const activityEvent = [...sortedEvents].toReversed().find((event) => event.type === "ActivityEvent");
|
|
54387
|
+
const contextSource = activityEvent || sortedEvents.at(-1);
|
|
54388
|
+
if (!contextSource) {
|
|
54329
54389
|
return null;
|
|
54330
54390
|
}
|
|
54331
|
-
const
|
|
54332
|
-
if (!
|
|
54391
|
+
const ctx = parseCaliperEventContext(contextSource, relevantCourseIds);
|
|
54392
|
+
if (!ctx) {
|
|
54333
54393
|
return null;
|
|
54334
54394
|
}
|
|
54335
|
-
|
|
54395
|
+
const score = activityEvent !== undefined ? (() => {
|
|
54396
|
+
const totalQuestions = getGeneratedMetricValue(activityEvent, "totalQuestions");
|
|
54397
|
+
const correctQuestions = getGeneratedMetricValue(activityEvent, "correctQuestions");
|
|
54398
|
+
if (totalQuestions === undefined || correctQuestions === undefined || totalQuestions <= 0) {
|
|
54399
|
+
return;
|
|
54400
|
+
}
|
|
54401
|
+
return correctQuestions / totalQuestions * 100;
|
|
54402
|
+
})() : undefined;
|
|
54403
|
+
const xpEarned = activityEvent !== undefined ? getGeneratedMetricValue(activityEvent, "xpEarned") : undefined;
|
|
54404
|
+
const masteredUnits = activityEvent !== undefined ? getGeneratedMetricValue(activityEvent, "masteredUnits") : undefined;
|
|
54405
|
+
const timeSpentEvents = sortedEvents.filter((event) => event.type === "TimeSpentEvent");
|
|
54406
|
+
let totalActiveTimeSeconds;
|
|
54407
|
+
if (timeSpentEvents.length > 0) {
|
|
54408
|
+
totalActiveTimeSeconds = timeSpentEvents.reduce((sum2, event) => sum2 + (getGeneratedMetricValue(event, "active") ?? 0), 0);
|
|
54409
|
+
} else if (activityEvent !== undefined) {
|
|
54410
|
+
totalActiveTimeSeconds = getDurationSecondsFromExtensions(activityEvent);
|
|
54411
|
+
}
|
|
54412
|
+
const fallbackActivityId = getActivityId(contextSource, getPlaycademyMetadata(contextSource));
|
|
54413
|
+
const occurredAt = getStringValue(activityEvent?.eventTime) || getStringValue(sortedEvents.at(-1)?.eventTime);
|
|
54414
|
+
const runId = getCanonicalRunId(contextSource.session);
|
|
54415
|
+
const resumeIds = new Set(sortedEvents.map((event) => getResumeId(event)).filter((resumeId) => resumeId !== undefined));
|
|
54416
|
+
const sessionCount = resumeIds.size > 0 ? resumeIds.size : 1;
|
|
54417
|
+
const kind = activityEvent !== undefined ? "activity" : "activity-in-progress";
|
|
54418
|
+
if (!occurredAt) {
|
|
54336
54419
|
return null;
|
|
54337
54420
|
}
|
|
54338
|
-
const metadata2 = isRecord2(assessment.metadata) ? assessment.metadata : undefined;
|
|
54339
|
-
const activityName = getStringValue(metadata2?.activityName);
|
|
54340
|
-
const xpEarned = typeof metadata2?.xp === "number" && Number.isFinite(metadata2.xp) ? metadata2.xp : undefined;
|
|
54341
|
-
const masteredUnits = typeof metadata2?.masteredUnits === "number" && Number.isFinite(metadata2.masteredUnits) ? metadata2.masteredUnits : undefined;
|
|
54342
|
-
const durationSeconds = typeof metadata2?.durationSeconds === "number" && Number.isFinite(metadata2.durationSeconds) ? metadata2.durationSeconds : undefined;
|
|
54343
54421
|
return {
|
|
54344
|
-
id:
|
|
54345
|
-
kind
|
|
54346
|
-
occurredAt
|
|
54347
|
-
courseId,
|
|
54348
|
-
title:
|
|
54349
|
-
...
|
|
54422
|
+
id: activityEvent?.externalId || sortedEvents.at(-1)?.externalId || events[0].externalId,
|
|
54423
|
+
kind,
|
|
54424
|
+
occurredAt,
|
|
54425
|
+
courseId: ctx.courseId,
|
|
54426
|
+
title: getStringValue(activityEvent?.object.activity?.name) || ctx.titleFromEvent || (fallbackActivityId ? kebabToTitleCase(fallbackActivityId) : "Activity completed"),
|
|
54427
|
+
...ctx.activityId ? { activityId: ctx.activityId } : {},
|
|
54428
|
+
...ctx.appName ? { appName: ctx.appName } : {},
|
|
54429
|
+
...score !== undefined ? { score } : {},
|
|
54350
54430
|
...xpEarned !== undefined ? { xpDelta: xpEarned } : {},
|
|
54351
54431
|
...masteredUnits !== undefined ? { masteredUnitsDelta: masteredUnits } : {},
|
|
54352
|
-
...
|
|
54432
|
+
...totalActiveTimeSeconds !== undefined ? { timeDeltaSeconds: totalActiveTimeSeconds } : {},
|
|
54433
|
+
...runId ? { runId } : {},
|
|
54434
|
+
...sessionCount > 0 ? { sessionCount } : {}
|
|
54353
54435
|
};
|
|
54354
54436
|
}
|
|
54355
54437
|
function parseCaliperEventContext(event, relevantCourseIds) {
|
|
@@ -54457,8 +54539,16 @@ function mapCaliperEventToRemediationActivity(event, relevantCourseIds) {
|
|
|
54457
54539
|
}
|
|
54458
54540
|
return null;
|
|
54459
54541
|
}
|
|
54542
|
+
var REMEDIATION_OR_COMPLETION_EVENT_KINDS;
|
|
54460
54543
|
var init_timeback_util = __esm(() => {
|
|
54461
54544
|
init_types4();
|
|
54545
|
+
REMEDIATION_OR_COMPLETION_EVENT_KINDS = new Set([
|
|
54546
|
+
"remediation-xp",
|
|
54547
|
+
"remediation-time",
|
|
54548
|
+
"remediation-mastery",
|
|
54549
|
+
"course-completed",
|
|
54550
|
+
"course-resumed"
|
|
54551
|
+
]);
|
|
54462
54552
|
});
|
|
54463
54553
|
|
|
54464
54554
|
class TimebackAdminService {
|
|
@@ -54467,11 +54557,9 @@ class TimebackAdminService {
|
|
|
54467
54557
|
static RECENT_ACTIVITY_LIMIT = 20;
|
|
54468
54558
|
static MAX_STUDENT_ACTIVITY_LIMIT = 200;
|
|
54469
54559
|
static MAX_STUDENT_ACTIVITY_OFFSET = 1000;
|
|
54560
|
+
static MAX_RECENT_ACTIVITY_EVENT_FETCH = 4000;
|
|
54470
54561
|
static ANALYTICS_CONCURRENCY = 8;
|
|
54471
54562
|
static MASTERABLE_UNITS_CONCURRENCY = 4;
|
|
54472
|
-
static RECENT_ACTIVITY_FETCH_CONCURRENCY = 4;
|
|
54473
|
-
static ASSESSMENT_LINE_ITEM_PAGE_SIZE = 1000;
|
|
54474
|
-
static ASSESSMENT_RESULT_LINE_ITEM_CHUNK_SIZE = 20;
|
|
54475
54563
|
constructor(deps) {
|
|
54476
54564
|
this.deps = deps;
|
|
54477
54565
|
}
|
|
@@ -54479,27 +54567,6 @@ class TimebackAdminService {
|
|
|
54479
54567
|
const rounded = Math.round(value * TimebackAdminService.XP_PRECISION_FACTOR) / TimebackAdminService.XP_PRECISION_FACTOR;
|
|
54480
54568
|
return Object.is(rounded, -0) ? 0 : rounded;
|
|
54481
54569
|
}
|
|
54482
|
-
static toAttributionEventTime(date3) {
|
|
54483
|
-
if (!date3) {
|
|
54484
|
-
return;
|
|
54485
|
-
}
|
|
54486
|
-
const match = date3.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
54487
|
-
if (!match) {
|
|
54488
|
-
throw new ValidationError("Date must be in YYYY-MM-DD format");
|
|
54489
|
-
}
|
|
54490
|
-
const [, yearStr, monthStr, dayStr] = match;
|
|
54491
|
-
const year = Number(yearStr);
|
|
54492
|
-
const month = Number(monthStr);
|
|
54493
|
-
const day = Number(dayStr);
|
|
54494
|
-
if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) {
|
|
54495
|
-
throw new ValidationError("Date must be in YYYY-MM-DD format");
|
|
54496
|
-
}
|
|
54497
|
-
const eventTime = new Date(Date.UTC(year, month - 1, day, 12, 0, 0));
|
|
54498
|
-
if (eventTime.getUTCFullYear() !== year || eventTime.getUTCMonth() + 1 !== month || eventTime.getUTCDate() !== day) {
|
|
54499
|
-
throw new ValidationError("Date must be a valid calendar date");
|
|
54500
|
-
}
|
|
54501
|
-
return eventTime.toISOString();
|
|
54502
|
-
}
|
|
54503
54570
|
requireClient() {
|
|
54504
54571
|
if (!this.deps.timeback) {
|
|
54505
54572
|
logger16.error("Timeback client not available in context");
|
|
@@ -54647,7 +54714,7 @@ class TimebackAdminService {
|
|
|
54647
54714
|
throw new ValidationError(`Game "${game.slug}" has an invalid deploymentUrl: ${game.deploymentUrl}`);
|
|
54648
54715
|
}
|
|
54649
54716
|
}
|
|
54650
|
-
async
|
|
54717
|
+
async getGameActivitySource(gameId) {
|
|
54651
54718
|
const game = await this.deps.db.query.games.findFirst({
|
|
54652
54719
|
where: eq(games.id, gameId),
|
|
54653
54720
|
columns: { slug: true, deploymentUrl: true }
|
|
@@ -54655,7 +54722,17 @@ class TimebackAdminService {
|
|
|
54655
54722
|
if (!game) {
|
|
54656
54723
|
throw new NotFoundError("Game", gameId);
|
|
54657
54724
|
}
|
|
54658
|
-
return
|
|
54725
|
+
return {
|
|
54726
|
+
gameId,
|
|
54727
|
+
sensorUrl: this.deriveGameSensorUrl(game),
|
|
54728
|
+
sourceMode: this.deps.config.isLocal ? "development" : "production"
|
|
54729
|
+
};
|
|
54730
|
+
}
|
|
54731
|
+
static mapRecentActivityItems(events, relevantCourseIds) {
|
|
54732
|
+
const gameplayEvents = events.filter((event) => (event.type === "ActivityEvent" || event.type === "TimeSpentEvent") && !isCaliperRemediationOrCompletionEvent(event));
|
|
54733
|
+
const groupedGameplayItems = [...groupCaliperEventsByRun(gameplayEvents).values()].map((group) => mapCaliperEventGroupToActivity(group, relevantCourseIds)).filter((item) => Boolean(item));
|
|
54734
|
+
const remediationItems = events.map((event) => mapCaliperEventToRemediationActivity(event, relevantCourseIds)).filter((item) => Boolean(item));
|
|
54735
|
+
return [...groupedGameplayItems, ...remediationItems].toSorted((a, b) => b.occurredAt.localeCompare(a.occurredAt));
|
|
54659
54736
|
}
|
|
54660
54737
|
async getStudentEnrollmentsByCourseId(client, studentId, courseIds) {
|
|
54661
54738
|
const relevantCourseIds = new Set(courseIds);
|
|
@@ -54686,101 +54763,31 @@ class TimebackAdminService {
|
|
|
54686
54763
|
});
|
|
54687
54764
|
return new Map(results);
|
|
54688
54765
|
}
|
|
54689
|
-
async
|
|
54690
|
-
const lineItemEntries = await TimebackAdminService.runWithConcurrency([...relevantCourseIds], TimebackAdminService.RECENT_ACTIVITY_FETCH_CONCURRENCY, async (courseId) => {
|
|
54691
|
-
const entries = [];
|
|
54692
|
-
let offset = 0;
|
|
54693
|
-
try {
|
|
54694
|
-
while (true) {
|
|
54695
|
-
const items2 = await client.oneroster.assessmentLineItems.list({
|
|
54696
|
-
limit: TimebackAdminService.ASSESSMENT_LINE_ITEM_PAGE_SIZE,
|
|
54697
|
-
offset,
|
|
54698
|
-
filter: `course.sourcedId='${escapeFilterValue(courseId)}'`,
|
|
54699
|
-
fields: "sourcedId,course"
|
|
54700
|
-
});
|
|
54701
|
-
for (const item of items2) {
|
|
54702
|
-
if (item.sourcedId) {
|
|
54703
|
-
entries.push([
|
|
54704
|
-
item.sourcedId,
|
|
54705
|
-
item.course?.sourcedId || courseId
|
|
54706
|
-
]);
|
|
54707
|
-
}
|
|
54708
|
-
}
|
|
54709
|
-
if (items2.length < TimebackAdminService.ASSESSMENT_LINE_ITEM_PAGE_SIZE) {
|
|
54710
|
-
break;
|
|
54711
|
-
}
|
|
54712
|
-
offset += TimebackAdminService.ASSESSMENT_LINE_ITEM_PAGE_SIZE;
|
|
54713
|
-
}
|
|
54714
|
-
} catch (error) {
|
|
54715
|
-
logger16.warn("Failed to load assessment line items for course", {
|
|
54716
|
-
courseId,
|
|
54717
|
-
error: error instanceof Error ? error.message : String(error)
|
|
54718
|
-
});
|
|
54719
|
-
}
|
|
54720
|
-
return entries;
|
|
54721
|
-
});
|
|
54722
|
-
return new Map(lineItemEntries.flat());
|
|
54723
|
-
}
|
|
54724
|
-
static buildAssessmentResultsFilter(studentId, lineItemIds) {
|
|
54725
|
-
const studentFilter = `student.sourcedId='${escapeFilterValue(studentId)}'`;
|
|
54726
|
-
if (lineItemIds.length === 1) {
|
|
54727
|
-
return `${studentFilter} AND assessmentLineItem.sourcedId='${escapeFilterValue(lineItemIds[0])}'`;
|
|
54728
|
-
}
|
|
54729
|
-
return `${studentFilter} AND assessmentLineItem.sourcedId@'${lineItemIds.map(escapeFilterValue).join(",")}'`;
|
|
54730
|
-
}
|
|
54731
|
-
async listRecentAssessmentResultsForStudent(client, studentId, courseIdByLineItemId, perChunkLimit = TimebackAdminService.RECENT_ACTIVITY_LIMIT) {
|
|
54732
|
-
const lineItemIds = [...courseIdByLineItemId.keys()];
|
|
54733
|
-
if (lineItemIds.length === 0) {
|
|
54734
|
-
return [];
|
|
54735
|
-
}
|
|
54736
|
-
const resultPages = await TimebackAdminService.runWithConcurrency(TimebackAdminService.chunkItems(lineItemIds, TimebackAdminService.ASSESSMENT_RESULT_LINE_ITEM_CHUNK_SIZE), TimebackAdminService.RECENT_ACTIVITY_FETCH_CONCURRENCY, async (lineItemChunk) => {
|
|
54737
|
-
try {
|
|
54738
|
-
return await client.oneroster.assessmentResults.list({
|
|
54739
|
-
limit: perChunkLimit,
|
|
54740
|
-
sort: "scoreDate",
|
|
54741
|
-
orderBy: "desc",
|
|
54742
|
-
fields: "sourcedId,assessmentLineItem,score,scoreDate,metadata",
|
|
54743
|
-
filter: TimebackAdminService.buildAssessmentResultsFilter(studentId, lineItemChunk)
|
|
54744
|
-
});
|
|
54745
|
-
} catch (error) {
|
|
54746
|
-
logger16.warn("Failed to load recent assessment results for student", {
|
|
54747
|
-
studentId,
|
|
54748
|
-
lineItemCount: lineItemChunk.length,
|
|
54749
|
-
error: error instanceof Error ? error.message : String(error)
|
|
54750
|
-
});
|
|
54751
|
-
return [];
|
|
54752
|
-
}
|
|
54753
|
-
});
|
|
54754
|
-
const uniqueResults = new Map;
|
|
54755
|
-
for (const result of resultPages.flat()) {
|
|
54756
|
-
const key = result.sourcedId || `${result.assessmentLineItem?.sourcedId || "unknown"}:${result.scoreDate || ""}`;
|
|
54757
|
-
uniqueResults.set(key, result);
|
|
54758
|
-
}
|
|
54759
|
-
return [...uniqueResults.values()].toSorted((a, b) => (b.scoreDate || "").localeCompare(a.scoreDate || "")).slice(0, perChunkLimit);
|
|
54760
|
-
}
|
|
54761
|
-
async listRecentActivityForStudent(client, studentId, sensorUrl, relevantCourseIds, maxResults = TimebackAdminService.RECENT_ACTIVITY_LIMIT) {
|
|
54766
|
+
async listRecentActivityForStudent(client, studentId, source, relevantCourseIds, maxResults = TimebackAdminService.RECENT_ACTIVITY_LIMIT) {
|
|
54762
54767
|
if (relevantCourseIds.size === 0) {
|
|
54763
54768
|
return [];
|
|
54764
54769
|
}
|
|
54765
|
-
const courseIdByLineItemId = await this.listAssessmentLineItemCourseMap(client, relevantCourseIds);
|
|
54766
|
-
const assessments = await this.listRecentAssessmentResultsForStudent(client, studentId, courseIdByLineItemId, maxResults);
|
|
54767
|
-
const assessmentRecentItems = assessments.map((assessment) => mapAssessmentResultToRecentActivity(assessment, relevantCourseIds, courseIdByLineItemId)).filter((activity) => Boolean(activity));
|
|
54768
|
-
let caliperRecentItems = [];
|
|
54769
54770
|
try {
|
|
54770
54771
|
const actorId = `${client.getBaseUrl().replace(/\/$/, "")}/ims/oneroster/rostering/v1p2/users/${studentId}`;
|
|
54772
|
+
const eventLimit = Math.min(Math.max(200, maxResults * 20), TimebackAdminService.MAX_RECENT_ACTIVITY_EVENT_FETCH);
|
|
54771
54773
|
const { events } = await client.caliper.events.list({
|
|
54772
|
-
limit:
|
|
54774
|
+
limit: eventLimit,
|
|
54773
54775
|
actorId,
|
|
54774
|
-
sensor: sensorUrl
|
|
54776
|
+
...source.sourceMode === "production" ? { sensor: source.sensorUrl } : {},
|
|
54777
|
+
extensions: {
|
|
54778
|
+
gameId: source.gameId
|
|
54779
|
+
}
|
|
54775
54780
|
});
|
|
54776
|
-
|
|
54781
|
+
return TimebackAdminService.mapRecentActivityItems(events, relevantCourseIds).slice(0, maxResults);
|
|
54777
54782
|
} catch (error) {
|
|
54778
54783
|
logger16.warn("Failed to load recent Caliper activity", {
|
|
54779
54784
|
studentId,
|
|
54785
|
+
gameId: source.gameId,
|
|
54786
|
+
sourceMode: source.sourceMode,
|
|
54780
54787
|
error: error instanceof Error ? error.message : String(error)
|
|
54781
54788
|
});
|
|
54789
|
+
return [];
|
|
54782
54790
|
}
|
|
54783
|
-
return [...assessmentRecentItems, ...caliperRecentItems].toSorted((a, b) => b.occurredAt.localeCompare(a.occurredAt)).slice(0, maxResults);
|
|
54784
54791
|
}
|
|
54785
54792
|
async listStudentsForCourse(gameId, courseId, user) {
|
|
54786
54793
|
const client = this.requireClient();
|
|
@@ -54877,11 +54884,11 @@ class TimebackAdminService {
|
|
|
54877
54884
|
const safeLimit = Math.max(1, Math.min(limit, TimebackAdminService.MAX_STUDENT_ACTIVITY_LIMIT));
|
|
54878
54885
|
const safeOffset = Math.max(0, Math.min(offset, TimebackAdminService.MAX_STUDENT_ACTIVITY_OFFSET));
|
|
54879
54886
|
await this.deps.validateGameManagementAccess(user, gameId);
|
|
54880
|
-
const [integration,
|
|
54887
|
+
const [integration, gameSource] = await Promise.all([
|
|
54881
54888
|
this.deps.db.query.gameTimebackIntegrations.findFirst({
|
|
54882
54889
|
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.courseId, courseId))
|
|
54883
54890
|
}),
|
|
54884
|
-
this.
|
|
54891
|
+
this.getGameActivitySource(gameId)
|
|
54885
54892
|
]);
|
|
54886
54893
|
if (!integration) {
|
|
54887
54894
|
throw new NotFoundError("Timeback integration", `${gameId}:${courseId}`);
|
|
@@ -54889,7 +54896,7 @@ class TimebackAdminService {
|
|
|
54889
54896
|
await this.assertStudentEnrolledInCourse(client, studentId, courseId);
|
|
54890
54897
|
const relevantCourseIds = new Set([courseId]);
|
|
54891
54898
|
const fetchLimit = Math.min(safeOffset + safeLimit + 1, TimebackAdminService.MAX_STUDENT_ACTIVITY_OFFSET + TimebackAdminService.MAX_STUDENT_ACTIVITY_LIMIT + 1);
|
|
54892
|
-
const allActivities = await this.listRecentActivityForStudent(client, studentId,
|
|
54899
|
+
const allActivities = await this.listRecentActivityForStudent(client, studentId, gameSource, relevantCourseIds, fetchLimit);
|
|
54893
54900
|
const activities = allActivities.slice(safeOffset, safeOffset + safeLimit);
|
|
54894
54901
|
const hasMore = allActivities.length > safeOffset + safeLimit;
|
|
54895
54902
|
return { activities, hasMore };
|
|
@@ -54901,7 +54908,7 @@ class TimebackAdminService {
|
|
|
54901
54908
|
courseId: data.courseId,
|
|
54902
54909
|
studentId: data.studentId,
|
|
54903
54910
|
xpEarned: data.xp,
|
|
54904
|
-
eventTime:
|
|
54911
|
+
eventTime: resolveAdminEventTime(data),
|
|
54905
54912
|
reason: data.reason,
|
|
54906
54913
|
actor,
|
|
54907
54914
|
appName,
|
|
@@ -54916,7 +54923,7 @@ class TimebackAdminService {
|
|
|
54916
54923
|
courseId: data.courseId,
|
|
54917
54924
|
studentId: data.studentId,
|
|
54918
54925
|
activeTimeSeconds: data.seconds,
|
|
54919
|
-
eventTime:
|
|
54926
|
+
eventTime: resolveAdminEventTime(data),
|
|
54920
54927
|
reason: data.reason,
|
|
54921
54928
|
actor,
|
|
54922
54929
|
appName,
|
|
@@ -54931,7 +54938,7 @@ class TimebackAdminService {
|
|
|
54931
54938
|
courseId: data.courseId,
|
|
54932
54939
|
studentId: data.studentId,
|
|
54933
54940
|
masteredUnits: data.units,
|
|
54934
|
-
eventTime:
|
|
54941
|
+
eventTime: resolveAdminEventTime(data),
|
|
54935
54942
|
reason: data.reason,
|
|
54936
54943
|
actor,
|
|
54937
54944
|
appName,
|
|
@@ -55142,17 +55149,6 @@ class TimebackAdminService {
|
|
|
55142
55149
|
}));
|
|
55143
55150
|
return results;
|
|
55144
55151
|
}
|
|
55145
|
-
static chunkItems(items2, chunkSize) {
|
|
55146
|
-
if (items2.length === 0) {
|
|
55147
|
-
return [];
|
|
55148
|
-
}
|
|
55149
|
-
const effectiveChunkSize = Math.max(1, chunkSize);
|
|
55150
|
-
const chunks = [];
|
|
55151
|
-
for (let index2 = 0;index2 < items2.length; index2 += effectiveChunkSize) {
|
|
55152
|
-
chunks.push(items2.slice(index2, index2 + effectiveChunkSize));
|
|
55153
|
-
}
|
|
55154
|
-
return chunks;
|
|
55155
|
-
}
|
|
55156
55152
|
}
|
|
55157
55153
|
var logger16;
|
|
55158
55154
|
var init_timeback_admin_service = __esm(() => {
|
|
@@ -55164,6 +55160,7 @@ var init_timeback_admin_service = __esm(() => {
|
|
|
55164
55160
|
init_utils6();
|
|
55165
55161
|
init_src4();
|
|
55166
55162
|
init_errors();
|
|
55163
|
+
init_timeback_admin_util();
|
|
55167
55164
|
init_timeback_util();
|
|
55168
55165
|
logger16 = log.scope("TimebackAdminService");
|
|
55169
55166
|
});
|
|
@@ -55207,6 +55204,18 @@ var init_timeback_service = __esm(() => {
|
|
|
55207
55204
|
static clearInFlightHeartbeatWindow(key) {
|
|
55208
55205
|
this.inFlightHeartbeatWindows.delete(key);
|
|
55209
55206
|
}
|
|
55207
|
+
static addResumeIdToExtensions(extensions, resumeId) {
|
|
55208
|
+
const base = extensions ?? {};
|
|
55209
|
+
const existingPlaycademy = base.playcademy;
|
|
55210
|
+
const playcademy = typeof existingPlaycademy === "object" && existingPlaycademy !== null && !Array.isArray(existingPlaycademy) ? existingPlaycademy : {};
|
|
55211
|
+
return {
|
|
55212
|
+
...base,
|
|
55213
|
+
playcademy: {
|
|
55214
|
+
...playcademy,
|
|
55215
|
+
resumeId
|
|
55216
|
+
}
|
|
55217
|
+
};
|
|
55218
|
+
}
|
|
55210
55219
|
constructor(deps) {
|
|
55211
55220
|
this.deps = deps;
|
|
55212
55221
|
}
|
|
@@ -55671,6 +55680,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55671
55680
|
gameId,
|
|
55672
55681
|
studentId,
|
|
55673
55682
|
runId,
|
|
55683
|
+
resumeId,
|
|
55674
55684
|
activityData,
|
|
55675
55685
|
scoreData,
|
|
55676
55686
|
timingData,
|
|
@@ -55682,6 +55692,8 @@ var init_timeback_service = __esm(() => {
|
|
|
55682
55692
|
}) {
|
|
55683
55693
|
const client = this.requireClient();
|
|
55684
55694
|
const db2 = this.deps.db;
|
|
55695
|
+
const effectiveResumeId = resumeId ?? runId ?? crypto.randomUUID();
|
|
55696
|
+
const extensionsWithResumeId = TimebackService2.addResumeIdToExtensions(extensions, effectiveResumeId);
|
|
55685
55697
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
55686
55698
|
const integration = await db2.query.gameTimebackIntegrations.findFirst({
|
|
55687
55699
|
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, activityData.grade), eq(gameTimebackIntegrations.subject, activityData.subject))
|
|
@@ -55691,13 +55703,14 @@ var init_timeback_service = __esm(() => {
|
|
|
55691
55703
|
}
|
|
55692
55704
|
const scorePercentage = scoreData.totalQuestions > 0 ? scoreData.correctQuestions / scoreData.totalQuestions * 100 : 0;
|
|
55693
55705
|
const result = await client.recordProgress(integration.courseId, studentId, {
|
|
55706
|
+
gameId,
|
|
55694
55707
|
score: scorePercentage,
|
|
55695
55708
|
totalQuestions: scoreData.totalQuestions,
|
|
55696
55709
|
correctQuestions: scoreData.correctQuestions,
|
|
55697
55710
|
durationSeconds: timingData.durationSeconds,
|
|
55698
55711
|
xpEarned,
|
|
55699
55712
|
masteredUnits,
|
|
55700
|
-
extensions,
|
|
55713
|
+
extensions: extensionsWithResumeId,
|
|
55701
55714
|
activityId: activityData.activityId,
|
|
55702
55715
|
activityName: activityData.activityName,
|
|
55703
55716
|
subject: activityData.subject,
|
|
@@ -55713,6 +55726,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55713
55726
|
const sessionEndInactiveSeconds = sessionTimingData?.inactiveSeconds;
|
|
55714
55727
|
if (sessionEndActiveSeconds > 0 || (sessionEndInactiveSeconds ?? 0) > 0) {
|
|
55715
55728
|
await client.recordSessionEnd(integration.courseId, studentId, {
|
|
55729
|
+
gameId,
|
|
55716
55730
|
activeTimeSeconds: sessionEndActiveSeconds,
|
|
55717
55731
|
...sessionEndInactiveSeconds !== undefined ? { inactiveTimeSeconds: sessionEndInactiveSeconds } : {},
|
|
55718
55732
|
activityId: activityData.activityId,
|
|
@@ -55723,6 +55737,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55723
55737
|
courseId: activityData.courseId,
|
|
55724
55738
|
courseName: activityData.courseName,
|
|
55725
55739
|
studentEmail: activityData.studentEmail,
|
|
55740
|
+
extensions: extensionsWithResumeId,
|
|
55726
55741
|
...runId ? { runId } : {}
|
|
55727
55742
|
});
|
|
55728
55743
|
}
|
|
@@ -55747,20 +55762,29 @@ var init_timeback_service = __esm(() => {
|
|
|
55747
55762
|
gameId,
|
|
55748
55763
|
studentId,
|
|
55749
55764
|
runId,
|
|
55765
|
+
resumeId,
|
|
55750
55766
|
activityData,
|
|
55751
55767
|
timingData,
|
|
55768
|
+
windowStartedAtMs,
|
|
55752
55769
|
windowSequence,
|
|
55753
55770
|
isFinal,
|
|
55754
55771
|
user
|
|
55755
55772
|
}) {
|
|
55756
55773
|
const client = this.requireClient();
|
|
55757
55774
|
const db2 = this.deps.db;
|
|
55758
|
-
const
|
|
55775
|
+
const hasWindowStartedAtMs = windowStartedAtMs !== undefined;
|
|
55776
|
+
const hasWindowSequence = windowSequence !== undefined;
|
|
55777
|
+
if (hasWindowStartedAtMs === hasWindowSequence) {
|
|
55778
|
+
throw new ValidationError("Provide exactly one of windowStartedAtMs or windowSequence");
|
|
55779
|
+
}
|
|
55780
|
+
const heartbeatWindowKey = hasWindowStartedAtMs ? `${runId}:t:${windowStartedAtMs}` : `${runId}:s:${windowSequence}`;
|
|
55781
|
+
const effectiveResumeId = resumeId ?? runId;
|
|
55759
55782
|
if (TimebackService2.isDuplicateHeartbeatWindow(heartbeatWindowKey)) {
|
|
55760
55783
|
logger17.debug("Skipping duplicate heartbeat window", {
|
|
55761
55784
|
gameId,
|
|
55762
55785
|
studentId,
|
|
55763
55786
|
runId,
|
|
55787
|
+
windowStartedAtMs,
|
|
55764
55788
|
windowSequence,
|
|
55765
55789
|
isFinal
|
|
55766
55790
|
});
|
|
@@ -55773,6 +55797,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55773
55797
|
gameId,
|
|
55774
55798
|
studentId,
|
|
55775
55799
|
runId,
|
|
55800
|
+
windowStartedAtMs,
|
|
55776
55801
|
windowSequence,
|
|
55777
55802
|
isFinal
|
|
55778
55803
|
});
|
|
@@ -55789,6 +55814,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55789
55814
|
const inactiveTimeSeconds = timingData.pausedMs / 1000;
|
|
55790
55815
|
if (activeTimeSeconds > 0 || inactiveTimeSeconds > 0) {
|
|
55791
55816
|
await client.recordSessionEnd(integration.courseId, studentId, {
|
|
55817
|
+
gameId,
|
|
55792
55818
|
activeTimeSeconds,
|
|
55793
55819
|
...inactiveTimeSeconds > 0 ? { inactiveTimeSeconds } : {},
|
|
55794
55820
|
activityId: activityData.activityId,
|
|
@@ -55799,6 +55825,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55799
55825
|
courseId: activityData.courseId,
|
|
55800
55826
|
courseName: activityData.courseName,
|
|
55801
55827
|
studentEmail: activityData.studentEmail,
|
|
55828
|
+
extensions: TimebackService2.addResumeIdToExtensions(undefined, effectiveResumeId),
|
|
55802
55829
|
...runId ? { runId } : {}
|
|
55803
55830
|
});
|
|
55804
55831
|
}
|
|
@@ -55808,6 +55835,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55808
55835
|
courseId: integration.courseId,
|
|
55809
55836
|
studentId,
|
|
55810
55837
|
runId,
|
|
55838
|
+
windowStartedAtMs,
|
|
55811
55839
|
windowSequence,
|
|
55812
55840
|
activeTimeSeconds,
|
|
55813
55841
|
isFinal
|
|
@@ -55959,6 +55987,7 @@ function createPlatformServices(deps) {
|
|
|
55959
55987
|
validateGameManagementAccess
|
|
55960
55988
|
});
|
|
55961
55989
|
const timebackAdmin = new TimebackAdminService({
|
|
55990
|
+
config: config2,
|
|
55962
55991
|
db: db2,
|
|
55963
55992
|
timeback: timebackClient,
|
|
55964
55993
|
validateDeveloperAccess,
|
|
@@ -58889,6 +58918,16 @@ async function requestCaliper(options) {
|
|
|
58889
58918
|
baseUrl: caliperUrl
|
|
58890
58919
|
});
|
|
58891
58920
|
}
|
|
58921
|
+
function buildEventExtensions({
|
|
58922
|
+
eventExtensions,
|
|
58923
|
+
gameId
|
|
58924
|
+
}) {
|
|
58925
|
+
const mergedExtensions = {
|
|
58926
|
+
...eventExtensions,
|
|
58927
|
+
...gameId ? { gameId } : {}
|
|
58928
|
+
};
|
|
58929
|
+
return Object.keys(mergedExtensions).length > 0 ? mergedExtensions : undefined;
|
|
58930
|
+
}
|
|
58892
58931
|
function createCaliperNamespace(client) {
|
|
58893
58932
|
const urls = createOneRosterUrls(client.getBaseUrl());
|
|
58894
58933
|
const caliper = {
|
|
@@ -58933,11 +58972,20 @@ function createCaliperNamespace(client) {
|
|
|
58933
58972
|
if (params.actorEmail) {
|
|
58934
58973
|
query.set("actorEmail", params.actorEmail);
|
|
58935
58974
|
}
|
|
58975
|
+
if (params.extensions) {
|
|
58976
|
+
for (const [key, value] of Object.entries(params.extensions)) {
|
|
58977
|
+
query.set(`extensions.${key}`, value);
|
|
58978
|
+
}
|
|
58979
|
+
}
|
|
58936
58980
|
const requestPath = `${CALIPER_ENDPOINTS4.events}?${query.toString()}`;
|
|
58937
58981
|
return client["requestCaliper"](requestPath, "GET");
|
|
58938
58982
|
}
|
|
58939
58983
|
},
|
|
58940
58984
|
emitActivityEvent: async (data) => {
|
|
58985
|
+
const eventExtensions = buildEventExtensions({
|
|
58986
|
+
eventExtensions: data.eventExtensions,
|
|
58987
|
+
gameId: data.gameId
|
|
58988
|
+
});
|
|
58941
58989
|
const event = {
|
|
58942
58990
|
"@context": CALIPER_CONSTANTS4.context,
|
|
58943
58991
|
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
@@ -58997,11 +59045,15 @@ function createCaliperNamespace(client) {
|
|
|
58997
59045
|
}
|
|
58998
59046
|
} : {}
|
|
58999
59047
|
},
|
|
59000
|
-
...
|
|
59048
|
+
...eventExtensions ? { extensions: eventExtensions } : {}
|
|
59001
59049
|
};
|
|
59002
59050
|
return caliper.emit(event, data.sensorUrl);
|
|
59003
59051
|
},
|
|
59004
59052
|
emitTimeSpentEvent: async (data) => {
|
|
59053
|
+
const eventExtensions = buildEventExtensions({
|
|
59054
|
+
eventExtensions: data.eventExtensions,
|
|
59055
|
+
gameId: data.gameId
|
|
59056
|
+
});
|
|
59005
59057
|
const event = {
|
|
59006
59058
|
"@context": CALIPER_CONSTANTS4.context,
|
|
59007
59059
|
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
@@ -59042,7 +59094,8 @@ function createCaliperNamespace(client) {
|
|
|
59042
59094
|
...data.wasteTimeSeconds !== undefined ? [{ type: TIME_METRIC_TYPES4.waste, value: data.wasteTimeSeconds }] : []
|
|
59043
59095
|
],
|
|
59044
59096
|
...data.extensions ? { extensions: data.extensions } : {}
|
|
59045
|
-
}
|
|
59097
|
+
},
|
|
59098
|
+
...eventExtensions ? { extensions: eventExtensions } : {}
|
|
59046
59099
|
};
|
|
59047
59100
|
return caliper.emit(event, data.sensorUrl);
|
|
59048
59101
|
},
|
|
@@ -59535,6 +59588,7 @@ class AdminEventRecorder {
|
|
|
59535
59588
|
await this.caliper.emitActivityEvent({
|
|
59536
59589
|
studentId: ctx.student.id,
|
|
59537
59590
|
studentEmail: ctx.student.email,
|
|
59591
|
+
gameId: data.gameId,
|
|
59538
59592
|
activityId: ctx.activityId,
|
|
59539
59593
|
activityName: data.activityName || `Manual XP Assignment - ${ctx.courseContext.subject}`,
|
|
59540
59594
|
courseId: data.courseId,
|
|
@@ -59561,6 +59615,7 @@ class AdminEventRecorder {
|
|
|
59561
59615
|
await this.caliper.emitTimeSpentEvent({
|
|
59562
59616
|
studentId: ctx.student.id,
|
|
59563
59617
|
studentEmail: ctx.student.email,
|
|
59618
|
+
gameId: data.gameId,
|
|
59564
59619
|
activityId: ctx.activityId,
|
|
59565
59620
|
activityName: data.activityName || "Playcademy Admin Time Adjustment",
|
|
59566
59621
|
courseId: data.courseId,
|
|
@@ -59582,6 +59637,7 @@ class AdminEventRecorder {
|
|
|
59582
59637
|
await this.caliper.emitActivityEvent({
|
|
59583
59638
|
studentId: ctx.student.id,
|
|
59584
59639
|
studentEmail: ctx.student.email,
|
|
59640
|
+
gameId: data.gameId,
|
|
59585
59641
|
activityId: ctx.activityId,
|
|
59586
59642
|
activityName: data.activityName || "Playcademy Admin Mastery Adjustment",
|
|
59587
59643
|
courseId: data.courseId,
|
|
@@ -59607,6 +59663,7 @@ class AdminEventRecorder {
|
|
|
59607
59663
|
await this.caliper.emitActivityEvent({
|
|
59608
59664
|
studentId: ctx.student.id,
|
|
59609
59665
|
studentEmail: ctx.student.email,
|
|
59666
|
+
gameId: data.gameId,
|
|
59610
59667
|
activityId: ctx.activityId,
|
|
59611
59668
|
activityName: isResume ? "Course resumed" : "Course marked complete",
|
|
59612
59669
|
courseId: data.courseId,
|
|
@@ -60055,15 +60112,13 @@ class ProgressRecorder {
|
|
|
60055
60112
|
studentId,
|
|
60056
60113
|
attemptNumber: currentAttemptNumber,
|
|
60057
60114
|
score,
|
|
60058
|
-
totalQuestions,
|
|
60059
|
-
correctQuestions,
|
|
60060
60115
|
xp: calculatedXp,
|
|
60061
|
-
masteredUnits,
|
|
60062
60116
|
scoreStatus,
|
|
60063
60117
|
inProgress,
|
|
60064
60118
|
appName: progressData.appName,
|
|
60065
|
-
|
|
60066
|
-
|
|
60119
|
+
totalQuestions,
|
|
60120
|
+
correctQuestions,
|
|
60121
|
+
masteredUnits
|
|
60067
60122
|
});
|
|
60068
60123
|
} else {
|
|
60069
60124
|
log.warn("[ProgressRecorder] Score not provided, skipping gradebook entry", {
|
|
@@ -60077,6 +60132,7 @@ class ProgressRecorder {
|
|
|
60077
60132
|
await this.emitCourseCompletionHistoryEvent({
|
|
60078
60133
|
studentId,
|
|
60079
60134
|
studentEmail,
|
|
60135
|
+
gameId: progressData.gameId,
|
|
60080
60136
|
activityId,
|
|
60081
60137
|
courseId: ids.course,
|
|
60082
60138
|
courseName,
|
|
@@ -60088,6 +60144,7 @@ class ProgressRecorder {
|
|
|
60088
60144
|
await this.emitCaliperEvent({
|
|
60089
60145
|
studentId,
|
|
60090
60146
|
studentEmail,
|
|
60147
|
+
gameId: progressData.gameId,
|
|
60091
60148
|
activityId,
|
|
60092
60149
|
activityName,
|
|
60093
60150
|
courseId: ids.course,
|
|
@@ -60189,15 +60246,13 @@ class ProgressRecorder {
|
|
|
60189
60246
|
studentId,
|
|
60190
60247
|
attemptNumber,
|
|
60191
60248
|
score,
|
|
60192
|
-
totalQuestions,
|
|
60193
|
-
correctQuestions,
|
|
60194
60249
|
xp,
|
|
60195
|
-
masteredUnits,
|
|
60196
60250
|
scoreStatus,
|
|
60197
60251
|
inProgress,
|
|
60198
60252
|
appName,
|
|
60199
|
-
|
|
60200
|
-
|
|
60253
|
+
totalQuestions,
|
|
60254
|
+
correctQuestions,
|
|
60255
|
+
masteredUnits
|
|
60201
60256
|
}) {
|
|
60202
60257
|
const timestamp3 = Date.now().toString(36);
|
|
60203
60258
|
const resultId = `${lineItemId}:${studentId}:${timestamp3}`;
|
|
@@ -60212,21 +60267,18 @@ class ProgressRecorder {
|
|
|
60212
60267
|
inProgress,
|
|
60213
60268
|
metadata: {
|
|
60214
60269
|
xp,
|
|
60215
|
-
totalQuestions,
|
|
60216
|
-
correctQuestions,
|
|
60217
|
-
accuracy: totalQuestions && correctQuestions ? correctQuestions / totalQuestions * 100 : undefined,
|
|
60218
60270
|
attemptNumber,
|
|
60219
|
-
lastUpdated: new Date().toISOString(),
|
|
60220
|
-
masteredUnits,
|
|
60221
60271
|
appName,
|
|
60222
|
-
|
|
60223
|
-
|
|
60272
|
+
...totalQuestions !== undefined ? { totalQuestions } : {},
|
|
60273
|
+
...correctQuestions !== undefined ? { correctQuestions } : {},
|
|
60274
|
+
...masteredUnits !== undefined ? { masteredUnits } : {}
|
|
60224
60275
|
}
|
|
60225
60276
|
});
|
|
60226
60277
|
}
|
|
60227
60278
|
async emitCaliperEvent({
|
|
60228
60279
|
studentId,
|
|
60229
60280
|
studentEmail,
|
|
60281
|
+
gameId,
|
|
60230
60282
|
activityId,
|
|
60231
60283
|
activityName,
|
|
60232
60284
|
courseId,
|
|
@@ -60243,6 +60295,7 @@ class ProgressRecorder {
|
|
|
60243
60295
|
await this.caliperNamespace.emitActivityEvent({
|
|
60244
60296
|
studentId,
|
|
60245
60297
|
studentEmail,
|
|
60298
|
+
gameId,
|
|
60246
60299
|
activityId,
|
|
60247
60300
|
activityName,
|
|
60248
60301
|
courseId,
|
|
@@ -60265,6 +60318,7 @@ class ProgressRecorder {
|
|
|
60265
60318
|
await this.caliperNamespace.emitActivityEvent({
|
|
60266
60319
|
studentId: data.studentId,
|
|
60267
60320
|
studentEmail: data.studentEmail,
|
|
60321
|
+
gameId: data.gameId,
|
|
60268
60322
|
activityId: data.activityId,
|
|
60269
60323
|
activityName: "Course completed",
|
|
60270
60324
|
courseId: data.courseId,
|
|
@@ -60314,6 +60368,7 @@ class SessionRecorder {
|
|
|
60314
60368
|
await this.caliperNamespace.emitTimeSpentEvent({
|
|
60315
60369
|
studentId,
|
|
60316
60370
|
studentEmail,
|
|
60371
|
+
gameId: sessionData.gameId,
|
|
60317
60372
|
activityId,
|
|
60318
60373
|
activityName,
|
|
60319
60374
|
courseId: ids.course,
|
|
@@ -118902,18 +118957,23 @@ async function seedCoreGames(db2) {
|
|
|
118902
118957
|
}
|
|
118903
118958
|
async function seedCurrentProjectGame(db2, project) {
|
|
118904
118959
|
const now2 = new Date;
|
|
118960
|
+
const desiredGameId = project.gameId?.trim() || undefined;
|
|
118905
118961
|
try {
|
|
118906
118962
|
const existingGame = await db2.query.games.findFirst({
|
|
118907
|
-
where: (row,
|
|
118963
|
+
where: (row, operators) => operators.eq(row.slug, project.slug)
|
|
118908
118964
|
});
|
|
118909
118965
|
if (existingGame) {
|
|
118910
|
-
if (
|
|
118911
|
-
await
|
|
118966
|
+
if (desiredGameId && existingGame.id !== desiredGameId) {
|
|
118967
|
+
await db2.delete(games).where(eq(games.id, existingGame.id));
|
|
118968
|
+
} else {
|
|
118969
|
+
if (project.timebackCourses && project.timebackCourses.length > 0) {
|
|
118970
|
+
await seedTimebackIntegrations(db2, existingGame.id, project.timebackCourses);
|
|
118971
|
+
}
|
|
118972
|
+
return existingGame;
|
|
118912
118973
|
}
|
|
118913
|
-
return existingGame;
|
|
118914
118974
|
}
|
|
118915
118975
|
const gameRecord = {
|
|
118916
|
-
id: crypto.randomUUID(),
|
|
118976
|
+
id: desiredGameId ?? crypto.randomUUID(),
|
|
118917
118977
|
developerId: DEMO_USERS.developer.id,
|
|
118918
118978
|
slug: project.slug,
|
|
118919
118979
|
displayName: project.displayName,
|
|
@@ -118942,6 +119002,7 @@ async function seedCurrentProjectGame(db2, project) {
|
|
|
118942
119002
|
}
|
|
118943
119003
|
}
|
|
118944
119004
|
var init_games = __esm(() => {
|
|
119005
|
+
init_drizzle_orm();
|
|
118945
119006
|
init_src();
|
|
118946
119007
|
init_tables_index();
|
|
118947
119008
|
init_constants();
|
|
@@ -120340,6 +120401,7 @@ var init_schemas11 = __esm(() => {
|
|
|
120340
120401
|
gameId: exports_external.string().uuid(),
|
|
120341
120402
|
studentId: exports_external.string().min(1),
|
|
120342
120403
|
runId: exports_external.string().uuid().optional(),
|
|
120404
|
+
resumeId: exports_external.string().uuid().optional(),
|
|
120343
120405
|
activityData: TimebackActivityDataSchema,
|
|
120344
120406
|
scoreData: exports_external.object({
|
|
120345
120407
|
correctQuestions: exports_external.number().int().min(0),
|
|
@@ -120360,13 +120422,18 @@ var init_schemas11 = __esm(() => {
|
|
|
120360
120422
|
gameId: exports_external.string().uuid(),
|
|
120361
120423
|
studentId: exports_external.string().min(1),
|
|
120362
120424
|
runId: exports_external.string().uuid(),
|
|
120425
|
+
resumeId: exports_external.string().uuid().optional(),
|
|
120363
120426
|
activityData: TimebackActivityDataSchema,
|
|
120364
120427
|
timingData: exports_external.object({
|
|
120365
120428
|
activeMs: exports_external.number().nonnegative(),
|
|
120366
120429
|
pausedMs: exports_external.number().nonnegative()
|
|
120367
120430
|
}),
|
|
120368
|
-
|
|
120431
|
+
windowStartedAtMs: exports_external.number().int().nonnegative().optional(),
|
|
120432
|
+
windowSequence: exports_external.number().int().nonnegative().optional(),
|
|
120369
120433
|
isFinal: exports_external.boolean().optional()
|
|
120434
|
+
}).refine((value) => value.windowStartedAtMs !== undefined !== (value.windowSequence !== undefined), {
|
|
120435
|
+
message: "Provide exactly one of windowStartedAtMs or windowSequence",
|
|
120436
|
+
path: ["windowStartedAtMs"]
|
|
120370
120437
|
});
|
|
120371
120438
|
PopulateStudentRequestSchema = exports_external.object({
|
|
120372
120439
|
firstName: exports_external.string().min(1).optional(),
|
|
@@ -120447,15 +120514,18 @@ var init_schemas11 = __esm(() => {
|
|
|
120447
120514
|
});
|
|
120448
120515
|
GrantTimebackXpRequestSchema = AdminTimebackMutationBaseSchema.extend({
|
|
120449
120516
|
xp: exports_external.number().min(-100, "Amount must be between -100 and 100").max(100, "Amount must be between -100 and 100").refine((value) => value !== 0, { message: "Amount cannot be 0" }),
|
|
120450
|
-
date: AdminAttributionDateSchema.optional()
|
|
120517
|
+
date: AdminAttributionDateSchema.optional(),
|
|
120518
|
+
useCurrentTime: exports_external.boolean().optional()
|
|
120451
120519
|
});
|
|
120452
120520
|
AdjustTimebackTimeRequestSchema = AdminTimebackMutationBaseSchema.extend({
|
|
120453
120521
|
seconds: exports_external.number().refine((value) => value !== 0, { message: "Time amount cannot be 0" }),
|
|
120454
|
-
date: AdminAttributionDateSchema.optional()
|
|
120522
|
+
date: AdminAttributionDateSchema.optional(),
|
|
120523
|
+
useCurrentTime: exports_external.boolean().optional()
|
|
120455
120524
|
});
|
|
120456
120525
|
AdjustTimebackMasteryRequestSchema = AdminTimebackMutationBaseSchema.extend({
|
|
120457
120526
|
units: exports_external.number().refine((value) => value !== 0, { message: "Units cannot be 0" }),
|
|
120458
|
-
date: AdminAttributionDateSchema.optional()
|
|
120527
|
+
date: AdminAttributionDateSchema.optional(),
|
|
120528
|
+
useCurrentTime: exports_external.boolean().optional()
|
|
120459
120529
|
});
|
|
120460
120530
|
ToggleCourseCompletionRequestSchema = exports_external.object({
|
|
120461
120531
|
gameId: exports_external.string().uuid(),
|
|
@@ -122836,6 +122906,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
122836
122906
|
gameId,
|
|
122837
122907
|
studentId,
|
|
122838
122908
|
runId,
|
|
122909
|
+
resumeId,
|
|
122839
122910
|
activityData,
|
|
122840
122911
|
scoreData,
|
|
122841
122912
|
timingData,
|
|
@@ -122849,6 +122920,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
122849
122920
|
gameId,
|
|
122850
122921
|
studentId,
|
|
122851
122922
|
runId,
|
|
122923
|
+
resumeId,
|
|
122852
122924
|
activityData,
|
|
122853
122925
|
scoreData,
|
|
122854
122926
|
timingData,
|
|
@@ -122872,11 +122944,23 @@ var init_timeback_controller = __esm(() => {
|
|
|
122872
122944
|
}
|
|
122873
122945
|
throw ApiError.badRequest("Invalid JSON body");
|
|
122874
122946
|
}
|
|
122875
|
-
const {
|
|
122947
|
+
const {
|
|
122948
|
+
gameId,
|
|
122949
|
+
studentId,
|
|
122950
|
+
runId,
|
|
122951
|
+
resumeId,
|
|
122952
|
+
activityData,
|
|
122953
|
+
timingData,
|
|
122954
|
+
windowStartedAtMs,
|
|
122955
|
+
windowSequence,
|
|
122956
|
+
isFinal
|
|
122957
|
+
} = body2;
|
|
122876
122958
|
logger63.debug("Recording heartbeat", {
|
|
122877
122959
|
userId: ctx.user.id,
|
|
122878
122960
|
gameId,
|
|
122879
122961
|
runId,
|
|
122962
|
+
resumeId,
|
|
122963
|
+
windowStartedAtMs,
|
|
122880
122964
|
windowSequence,
|
|
122881
122965
|
activeMs: timingData.activeMs,
|
|
122882
122966
|
isFinal
|
|
@@ -122885,8 +122969,10 @@ var init_timeback_controller = __esm(() => {
|
|
|
122885
122969
|
gameId,
|
|
122886
122970
|
studentId,
|
|
122887
122971
|
runId,
|
|
122972
|
+
resumeId,
|
|
122888
122973
|
activityData,
|
|
122889
122974
|
timingData,
|
|
122975
|
+
windowStartedAtMs,
|
|
122890
122976
|
windowSequence,
|
|
122891
122977
|
isFinal,
|
|
122892
122978
|
user: ctx.user
|
|
@@ -124471,6 +124557,203 @@ function printBanner(viteConfig, options) {
|
|
|
124471
124557
|
import fs5 from "node:fs";
|
|
124472
124558
|
import path3 from "node:path";
|
|
124473
124559
|
import { loadPlaycademyConfig } from "playcademy/utils";
|
|
124560
|
+
|
|
124561
|
+
// ../utils/src/uuid.ts
|
|
124562
|
+
var UUID_REGEX2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
124563
|
+
function isValidUUID2(value) {
|
|
124564
|
+
if (!value || typeof value !== "string") {
|
|
124565
|
+
return false;
|
|
124566
|
+
}
|
|
124567
|
+
return UUID_REGEX2.test(value);
|
|
124568
|
+
}
|
|
124569
|
+
// ../utils/src/ansi.ts
|
|
124570
|
+
var colors3 = {
|
|
124571
|
+
black: "\x1B[30m",
|
|
124572
|
+
red: "\x1B[31m",
|
|
124573
|
+
green: "\x1B[32m",
|
|
124574
|
+
yellow: "\x1B[33m",
|
|
124575
|
+
blue: "\x1B[34m",
|
|
124576
|
+
magenta: "\x1B[35m",
|
|
124577
|
+
cyan: "\x1B[36m",
|
|
124578
|
+
white: "\x1B[37m",
|
|
124579
|
+
gray: "\x1B[90m"
|
|
124580
|
+
};
|
|
124581
|
+
var styles3 = {
|
|
124582
|
+
reset: "\x1B[0m",
|
|
124583
|
+
bold: "\x1B[1m",
|
|
124584
|
+
dim: "\x1B[2m",
|
|
124585
|
+
italic: "\x1B[3m",
|
|
124586
|
+
underline: "\x1B[4m"
|
|
124587
|
+
};
|
|
124588
|
+
var cursor2 = {
|
|
124589
|
+
hide: "\x1B[?25l",
|
|
124590
|
+
show: "\x1B[?25h",
|
|
124591
|
+
up: (n3) => `\x1B[${n3}A`,
|
|
124592
|
+
down: (n3) => `\x1B[${n3}B`,
|
|
124593
|
+
forward: (n3) => `\x1B[${n3}C`,
|
|
124594
|
+
back: (n3) => `\x1B[${n3}D`,
|
|
124595
|
+
clearLine: "\x1B[K",
|
|
124596
|
+
clearScreen: "\x1B[2J",
|
|
124597
|
+
home: "\x1B[H"
|
|
124598
|
+
};
|
|
124599
|
+
var isInteractive2 = typeof process !== "undefined" && process.stdout?.isTTY && !process.env.CI && process.env.TERM !== "dumb";
|
|
124600
|
+
function stripAnsi2(text2) {
|
|
124601
|
+
return text2.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
|
|
124602
|
+
}
|
|
124603
|
+
|
|
124604
|
+
// ../utils/src/spinner.ts
|
|
124605
|
+
import { stdout as stdout2 } from "process";
|
|
124606
|
+
var SPINNER_FRAMES2 = [
|
|
124607
|
+
10251,
|
|
124608
|
+
10265,
|
|
124609
|
+
10297,
|
|
124610
|
+
10296,
|
|
124611
|
+
10300,
|
|
124612
|
+
10292,
|
|
124613
|
+
10278,
|
|
124614
|
+
10279,
|
|
124615
|
+
10247,
|
|
124616
|
+
10255
|
|
124617
|
+
].map((code) => String.fromCodePoint(code));
|
|
124618
|
+
var CHECK_MARK2 = String.fromCodePoint(10004);
|
|
124619
|
+
var CROSS_MARK2 = String.fromCodePoint(10006);
|
|
124620
|
+
var CANCEL_MARK2 = String.fromCodePoint(9675);
|
|
124621
|
+
var SPINNER_INTERVAL2 = 80;
|
|
124622
|
+
|
|
124623
|
+
class Spinner3 {
|
|
124624
|
+
tasks = new Map;
|
|
124625
|
+
frameIndex = 0;
|
|
124626
|
+
intervalId = null;
|
|
124627
|
+
renderCount = 0;
|
|
124628
|
+
previousLineCount = 0;
|
|
124629
|
+
printedTasks = new Set;
|
|
124630
|
+
indent;
|
|
124631
|
+
constructor(taskIds, texts, options) {
|
|
124632
|
+
this.indent = options?.indent ?? 0;
|
|
124633
|
+
taskIds.forEach((id, index6) => {
|
|
124634
|
+
this.tasks.set(id, {
|
|
124635
|
+
text: texts[index6] || "",
|
|
124636
|
+
status: "pending"
|
|
124637
|
+
});
|
|
124638
|
+
});
|
|
124639
|
+
}
|
|
124640
|
+
start() {
|
|
124641
|
+
if (isInteractive2) {
|
|
124642
|
+
stdout2.write(cursor2.hide);
|
|
124643
|
+
this.render();
|
|
124644
|
+
this.intervalId = setInterval(() => {
|
|
124645
|
+
this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES2.length;
|
|
124646
|
+
this.render();
|
|
124647
|
+
}, SPINNER_INTERVAL2);
|
|
124648
|
+
}
|
|
124649
|
+
}
|
|
124650
|
+
clear() {
|
|
124651
|
+
if (this.intervalId) {
|
|
124652
|
+
clearInterval(this.intervalId);
|
|
124653
|
+
this.intervalId = null;
|
|
124654
|
+
}
|
|
124655
|
+
if (isInteractive2 && this.previousLineCount > 0) {
|
|
124656
|
+
stdout2.write(cursor2.up(this.previousLineCount));
|
|
124657
|
+
for (let i3 = 0;i3 < this.previousLineCount; i3++) {
|
|
124658
|
+
stdout2.write(`\r${cursor2.clearLine}
|
|
124659
|
+
`);
|
|
124660
|
+
}
|
|
124661
|
+
stdout2.write(cursor2.up(this.previousLineCount));
|
|
124662
|
+
stdout2.write(cursor2.show);
|
|
124663
|
+
}
|
|
124664
|
+
this.previousLineCount = 0;
|
|
124665
|
+
}
|
|
124666
|
+
updateTask(taskId, status, finalText) {
|
|
124667
|
+
const task = this.tasks.get(taskId);
|
|
124668
|
+
if (task) {
|
|
124669
|
+
task.status = status;
|
|
124670
|
+
if (finalText) {
|
|
124671
|
+
task.finalText = finalText;
|
|
124672
|
+
}
|
|
124673
|
+
if (!isInteractive2) {
|
|
124674
|
+
this.renderNonInteractive(taskId, task);
|
|
124675
|
+
}
|
|
124676
|
+
}
|
|
124677
|
+
}
|
|
124678
|
+
renderNonInteractive(taskId, task) {
|
|
124679
|
+
const key = `${taskId}-${task.status}`;
|
|
124680
|
+
if (this.printedTasks.has(key)) {
|
|
124681
|
+
return;
|
|
124682
|
+
}
|
|
124683
|
+
this.printedTasks.add(key);
|
|
124684
|
+
const indentStr = " ".repeat(this.indent);
|
|
124685
|
+
let line2 = "";
|
|
124686
|
+
switch (task.status) {
|
|
124687
|
+
case "running": {
|
|
124688
|
+
line2 = `${indentStr}[RUNNING] ${stripAnsi2(task.text)}`;
|
|
124689
|
+
break;
|
|
124690
|
+
}
|
|
124691
|
+
case "success": {
|
|
124692
|
+
line2 = `${indentStr}[SUCCESS] ${stripAnsi2(task.finalText || task.text)}`;
|
|
124693
|
+
break;
|
|
124694
|
+
}
|
|
124695
|
+
case "error": {
|
|
124696
|
+
line2 = `${indentStr}[ERROR] Failed: ${stripAnsi2(task.text)}`;
|
|
124697
|
+
break;
|
|
124698
|
+
}
|
|
124699
|
+
case "cancelled": {
|
|
124700
|
+
line2 = `${indentStr}[CANCELLED] ${stripAnsi2(task.finalText || task.text)}`;
|
|
124701
|
+
break;
|
|
124702
|
+
}
|
|
124703
|
+
}
|
|
124704
|
+
console.log(line2);
|
|
124705
|
+
}
|
|
124706
|
+
render() {
|
|
124707
|
+
if (this.previousLineCount > 0) {
|
|
124708
|
+
stdout2.write(cursor2.up(this.previousLineCount));
|
|
124709
|
+
}
|
|
124710
|
+
const spinner = SPINNER_FRAMES2[this.frameIndex];
|
|
124711
|
+
const indentStr = " ".repeat(this.indent);
|
|
124712
|
+
const visibleTasks = [...this.tasks.values()].filter((task) => task.status !== "pending");
|
|
124713
|
+
for (const task of visibleTasks) {
|
|
124714
|
+
stdout2.write(`\r${cursor2.clearLine}`);
|
|
124715
|
+
let line2 = "";
|
|
124716
|
+
switch (task.status) {
|
|
124717
|
+
case "running": {
|
|
124718
|
+
line2 = `${indentStr}${colors3.blue}${spinner}${styles3.reset} ${task.text}`;
|
|
124719
|
+
break;
|
|
124720
|
+
}
|
|
124721
|
+
case "success": {
|
|
124722
|
+
line2 = `${indentStr}${colors3.green}${CHECK_MARK2}${styles3.reset} ${task.finalText || task.text}`;
|
|
124723
|
+
break;
|
|
124724
|
+
}
|
|
124725
|
+
case "error": {
|
|
124726
|
+
line2 = `${indentStr}${colors3.red}${CROSS_MARK2}${styles3.reset} Failed: ${task.text}`;
|
|
124727
|
+
break;
|
|
124728
|
+
}
|
|
124729
|
+
case "cancelled": {
|
|
124730
|
+
line2 = `${indentStr}${colors3.gray}${CANCEL_MARK2}${styles3.reset} Cancelled: ${task.finalText || task.text}`;
|
|
124731
|
+
break;
|
|
124732
|
+
}
|
|
124733
|
+
}
|
|
124734
|
+
console.log(line2);
|
|
124735
|
+
}
|
|
124736
|
+
this.previousLineCount = visibleTasks.length;
|
|
124737
|
+
this.renderCount++;
|
|
124738
|
+
}
|
|
124739
|
+
stop() {
|
|
124740
|
+
if (this.intervalId) {
|
|
124741
|
+
clearInterval(this.intervalId);
|
|
124742
|
+
this.intervalId = null;
|
|
124743
|
+
}
|
|
124744
|
+
if (isInteractive2) {
|
|
124745
|
+
this.render();
|
|
124746
|
+
stdout2.write(cursor2.show);
|
|
124747
|
+
} else {
|
|
124748
|
+
this.tasks.forEach((task, taskId) => {
|
|
124749
|
+
if (task.status !== "pending") {
|
|
124750
|
+
this.renderNonInteractive(taskId, task);
|
|
124751
|
+
}
|
|
124752
|
+
});
|
|
124753
|
+
}
|
|
124754
|
+
}
|
|
124755
|
+
}
|
|
124756
|
+
// src/lib/sandbox/project-info.ts
|
|
124474
124757
|
function extractTimebackCourses(config2, timebackOptions) {
|
|
124475
124758
|
const courses = config2?.integrations?.timeback?.courses;
|
|
124476
124759
|
if (!courses || courses.length === 0) {
|
|
@@ -124504,17 +124787,20 @@ async function extractProjectInfo(viteConfig, timebackOptions) {
|
|
|
124504
124787
|
packageJson = JSON.parse(packageJsonContent);
|
|
124505
124788
|
}
|
|
124506
124789
|
} catch {}
|
|
124507
|
-
const
|
|
124508
|
-
let
|
|
124509
|
-
if (
|
|
124510
|
-
|
|
124790
|
+
const name4 = config2?.name || packageJson.name || "";
|
|
124791
|
+
let slug2 = name4;
|
|
124792
|
+
if (slug2.includes("/")) {
|
|
124793
|
+
slug2 = slug2.split("/")[1] || slug2;
|
|
124511
124794
|
}
|
|
124512
|
-
if (!
|
|
124513
|
-
|
|
124795
|
+
if (!slug2) {
|
|
124796
|
+
slug2 = directoryName;
|
|
124514
124797
|
}
|
|
124515
|
-
const displayName =
|
|
124798
|
+
const displayName = slug2.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
124799
|
+
const envGameId = process.env.SANDBOX_GAME_ID;
|
|
124800
|
+
const gameId = envGameId && isValidUUID2(envGameId) ? envGameId : undefined;
|
|
124516
124801
|
return {
|
|
124517
|
-
|
|
124802
|
+
gameId,
|
|
124803
|
+
slug: slug2,
|
|
124518
124804
|
displayName,
|
|
124519
124805
|
version: packageJson.version || "dev",
|
|
124520
124806
|
description: packageJson.description,
|
|
@@ -125549,7 +125835,7 @@ var DEBOUNCE_MS = 500;
|
|
|
125549
125835
|
var VITE_CONFIG_NAMES = ["vite.config.ts", "vite.config.js", "vite.config.mjs"];
|
|
125550
125836
|
var debounceTimer = null;
|
|
125551
125837
|
function findExistingFiles(projectRoot, fileNames) {
|
|
125552
|
-
return fileNames.map((
|
|
125838
|
+
return fileNames.map((name4) => path4.join(projectRoot, name4)).filter((file) => fs7.existsSync(file));
|
|
125553
125839
|
}
|
|
125554
125840
|
function createChangeHandler(server, viteConfig, platformModeOptions, watchedFiles) {
|
|
125555
125841
|
return async (changedPath) => {
|
|
@@ -125617,7 +125903,7 @@ function cyclePlatformRoleHotkey(options) {
|
|
|
125617
125903
|
|
|
125618
125904
|
// src/server/hotkeys/cycle-timeback-role.ts
|
|
125619
125905
|
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
125620
|
-
var { bold:
|
|
125906
|
+
var { bold: bold5, cyan: cyan4, green: green4, red: red3, yellow: yellow3 } = import_picocolors9.default;
|
|
125621
125907
|
function cycleTimebackRole(logger) {
|
|
125622
125908
|
const currentRole = getTimebackRoleOverride() ?? "student";
|
|
125623
125909
|
const currentIndex = TIMEBACK_ROLES.indexOf(currentRole);
|
|
@@ -125636,14 +125922,14 @@ function cycleTimebackRole(logger) {
|
|
|
125636
125922
|
function cycleTimebackRoleHotkey(options) {
|
|
125637
125923
|
return {
|
|
125638
125924
|
key: "t",
|
|
125639
|
-
description: `${cyan4(
|
|
125925
|
+
description: `${cyan4(bold5("[playcademy]"))} cycle Timeback role`,
|
|
125640
125926
|
action: () => cycleTimebackRole(options.viteConfig.logger)
|
|
125641
125927
|
};
|
|
125642
125928
|
}
|
|
125643
125929
|
|
|
125644
125930
|
// src/server/hotkeys/recreate-database.ts
|
|
125645
125931
|
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
125646
|
-
var { bold:
|
|
125932
|
+
var { bold: bold6, cyan: cyan5 } = import_picocolors10.default;
|
|
125647
125933
|
async function recreateSandboxDatabase(options) {
|
|
125648
125934
|
await recreateSandbox({
|
|
125649
125935
|
viteConfig: options.viteConfig,
|
|
@@ -125653,7 +125939,7 @@ async function recreateSandboxDatabase(options) {
|
|
|
125653
125939
|
function recreateDatabaseHotkey(options) {
|
|
125654
125940
|
return {
|
|
125655
125941
|
key: "d",
|
|
125656
|
-
description: `${cyan5(
|
|
125942
|
+
description: `${cyan5(bold6("[playcademy]"))} recreate sandbox database`,
|
|
125657
125943
|
action: () => recreateSandboxDatabase(options)
|
|
125658
125944
|
};
|
|
125659
125945
|
}
|
|
@@ -125663,7 +125949,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
|
125663
125949
|
// package.json
|
|
125664
125950
|
var package_default2 = {
|
|
125665
125951
|
name: "@playcademy/vite-plugin",
|
|
125666
|
-
version: "0.2.24-beta.
|
|
125952
|
+
version: "0.2.24-beta.8",
|
|
125667
125953
|
type: "module",
|
|
125668
125954
|
exports: {
|
|
125669
125955
|
".": {
|
package/dist/types/internal.d.ts
CHANGED