@playcademy/vite-plugin 0.2.24-beta.5 → 0.2.24-beta.7
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 +474 -206
- 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.10",
|
|
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,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55682
55692
|
}) {
|
|
55683
55693
|
const client = this.requireClient();
|
|
55684
55694
|
const db2 = this.deps.db;
|
|
55695
|
+
const extensionsWithResumeId = TimebackService2.addResumeIdToExtensions(extensions, resumeId);
|
|
55685
55696
|
await this.deps.validateDeveloperAccess(user, gameId);
|
|
55686
55697
|
const integration = await db2.query.gameTimebackIntegrations.findFirst({
|
|
55687
55698
|
where: and(eq(gameTimebackIntegrations.gameId, gameId), eq(gameTimebackIntegrations.grade, activityData.grade), eq(gameTimebackIntegrations.subject, activityData.subject))
|
|
@@ -55691,13 +55702,14 @@ var init_timeback_service = __esm(() => {
|
|
|
55691
55702
|
}
|
|
55692
55703
|
const scorePercentage = scoreData.totalQuestions > 0 ? scoreData.correctQuestions / scoreData.totalQuestions * 100 : 0;
|
|
55693
55704
|
const result = await client.recordProgress(integration.courseId, studentId, {
|
|
55705
|
+
gameId,
|
|
55694
55706
|
score: scorePercentage,
|
|
55695
55707
|
totalQuestions: scoreData.totalQuestions,
|
|
55696
55708
|
correctQuestions: scoreData.correctQuestions,
|
|
55697
55709
|
durationSeconds: timingData.durationSeconds,
|
|
55698
55710
|
xpEarned,
|
|
55699
55711
|
masteredUnits,
|
|
55700
|
-
extensions,
|
|
55712
|
+
extensions: extensionsWithResumeId,
|
|
55701
55713
|
activityId: activityData.activityId,
|
|
55702
55714
|
activityName: activityData.activityName,
|
|
55703
55715
|
subject: activityData.subject,
|
|
@@ -55713,6 +55725,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55713
55725
|
const sessionEndInactiveSeconds = sessionTimingData?.inactiveSeconds;
|
|
55714
55726
|
if (sessionEndActiveSeconds > 0 || (sessionEndInactiveSeconds ?? 0) > 0) {
|
|
55715
55727
|
await client.recordSessionEnd(integration.courseId, studentId, {
|
|
55728
|
+
gameId,
|
|
55716
55729
|
activeTimeSeconds: sessionEndActiveSeconds,
|
|
55717
55730
|
...sessionEndInactiveSeconds !== undefined ? { inactiveTimeSeconds: sessionEndInactiveSeconds } : {},
|
|
55718
55731
|
activityId: activityData.activityId,
|
|
@@ -55723,6 +55736,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55723
55736
|
courseId: activityData.courseId,
|
|
55724
55737
|
courseName: activityData.courseName,
|
|
55725
55738
|
studentEmail: activityData.studentEmail,
|
|
55739
|
+
extensions: extensionsWithResumeId,
|
|
55726
55740
|
...runId ? { runId } : {}
|
|
55727
55741
|
});
|
|
55728
55742
|
}
|
|
@@ -55747,21 +55761,22 @@ var init_timeback_service = __esm(() => {
|
|
|
55747
55761
|
gameId,
|
|
55748
55762
|
studentId,
|
|
55749
55763
|
runId,
|
|
55764
|
+
resumeId,
|
|
55750
55765
|
activityData,
|
|
55751
55766
|
timingData,
|
|
55752
|
-
|
|
55767
|
+
windowStartedAtMs,
|
|
55753
55768
|
isFinal,
|
|
55754
55769
|
user
|
|
55755
55770
|
}) {
|
|
55756
55771
|
const client = this.requireClient();
|
|
55757
55772
|
const db2 = this.deps.db;
|
|
55758
|
-
const heartbeatWindowKey = `${runId}:${
|
|
55773
|
+
const heartbeatWindowKey = `${runId}:${windowStartedAtMs}`;
|
|
55759
55774
|
if (TimebackService2.isDuplicateHeartbeatWindow(heartbeatWindowKey)) {
|
|
55760
55775
|
logger17.debug("Skipping duplicate heartbeat window", {
|
|
55761
55776
|
gameId,
|
|
55762
55777
|
studentId,
|
|
55763
55778
|
runId,
|
|
55764
|
-
|
|
55779
|
+
windowStartedAtMs,
|
|
55765
55780
|
isFinal
|
|
55766
55781
|
});
|
|
55767
55782
|
return { status: "ok" };
|
|
@@ -55773,7 +55788,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55773
55788
|
gameId,
|
|
55774
55789
|
studentId,
|
|
55775
55790
|
runId,
|
|
55776
|
-
|
|
55791
|
+
windowStartedAtMs,
|
|
55777
55792
|
isFinal
|
|
55778
55793
|
});
|
|
55779
55794
|
return inFlightHeartbeat;
|
|
@@ -55789,6 +55804,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55789
55804
|
const inactiveTimeSeconds = timingData.pausedMs / 1000;
|
|
55790
55805
|
if (activeTimeSeconds > 0 || inactiveTimeSeconds > 0) {
|
|
55791
55806
|
await client.recordSessionEnd(integration.courseId, studentId, {
|
|
55807
|
+
gameId,
|
|
55792
55808
|
activeTimeSeconds,
|
|
55793
55809
|
...inactiveTimeSeconds > 0 ? { inactiveTimeSeconds } : {},
|
|
55794
55810
|
activityId: activityData.activityId,
|
|
@@ -55799,6 +55815,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55799
55815
|
courseId: activityData.courseId,
|
|
55800
55816
|
courseName: activityData.courseName,
|
|
55801
55817
|
studentEmail: activityData.studentEmail,
|
|
55818
|
+
extensions: TimebackService2.addResumeIdToExtensions(undefined, resumeId),
|
|
55802
55819
|
...runId ? { runId } : {}
|
|
55803
55820
|
});
|
|
55804
55821
|
}
|
|
@@ -55808,7 +55825,7 @@ var init_timeback_service = __esm(() => {
|
|
|
55808
55825
|
courseId: integration.courseId,
|
|
55809
55826
|
studentId,
|
|
55810
55827
|
runId,
|
|
55811
|
-
|
|
55828
|
+
windowStartedAtMs,
|
|
55812
55829
|
activeTimeSeconds,
|
|
55813
55830
|
isFinal
|
|
55814
55831
|
});
|
|
@@ -55959,6 +55976,7 @@ function createPlatformServices(deps) {
|
|
|
55959
55976
|
validateGameManagementAccess
|
|
55960
55977
|
});
|
|
55961
55978
|
const timebackAdmin = new TimebackAdminService({
|
|
55979
|
+
config: config2,
|
|
55962
55980
|
db: db2,
|
|
55963
55981
|
timeback: timebackClient,
|
|
55964
55982
|
validateDeveloperAccess,
|
|
@@ -58889,6 +58907,16 @@ async function requestCaliper(options) {
|
|
|
58889
58907
|
baseUrl: caliperUrl
|
|
58890
58908
|
});
|
|
58891
58909
|
}
|
|
58910
|
+
function buildEventExtensions({
|
|
58911
|
+
eventExtensions,
|
|
58912
|
+
gameId
|
|
58913
|
+
}) {
|
|
58914
|
+
const mergedExtensions = {
|
|
58915
|
+
...eventExtensions,
|
|
58916
|
+
...gameId ? { gameId } : {}
|
|
58917
|
+
};
|
|
58918
|
+
return Object.keys(mergedExtensions).length > 0 ? mergedExtensions : undefined;
|
|
58919
|
+
}
|
|
58892
58920
|
function createCaliperNamespace(client) {
|
|
58893
58921
|
const urls = createOneRosterUrls(client.getBaseUrl());
|
|
58894
58922
|
const caliper = {
|
|
@@ -58933,11 +58961,20 @@ function createCaliperNamespace(client) {
|
|
|
58933
58961
|
if (params.actorEmail) {
|
|
58934
58962
|
query.set("actorEmail", params.actorEmail);
|
|
58935
58963
|
}
|
|
58964
|
+
if (params.extensions) {
|
|
58965
|
+
for (const [key, value] of Object.entries(params.extensions)) {
|
|
58966
|
+
query.set(`extensions.${key}`, value);
|
|
58967
|
+
}
|
|
58968
|
+
}
|
|
58936
58969
|
const requestPath = `${CALIPER_ENDPOINTS4.events}?${query.toString()}`;
|
|
58937
58970
|
return client["requestCaliper"](requestPath, "GET");
|
|
58938
58971
|
}
|
|
58939
58972
|
},
|
|
58940
58973
|
emitActivityEvent: async (data) => {
|
|
58974
|
+
const eventExtensions = buildEventExtensions({
|
|
58975
|
+
eventExtensions: data.eventExtensions,
|
|
58976
|
+
gameId: data.gameId
|
|
58977
|
+
});
|
|
58941
58978
|
const event = {
|
|
58942
58979
|
"@context": CALIPER_CONSTANTS4.context,
|
|
58943
58980
|
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
@@ -58997,11 +59034,15 @@ function createCaliperNamespace(client) {
|
|
|
58997
59034
|
}
|
|
58998
59035
|
} : {}
|
|
58999
59036
|
},
|
|
59000
|
-
...
|
|
59037
|
+
...eventExtensions ? { extensions: eventExtensions } : {}
|
|
59001
59038
|
};
|
|
59002
59039
|
return caliper.emit(event, data.sensorUrl);
|
|
59003
59040
|
},
|
|
59004
59041
|
emitTimeSpentEvent: async (data) => {
|
|
59042
|
+
const eventExtensions = buildEventExtensions({
|
|
59043
|
+
eventExtensions: data.eventExtensions,
|
|
59044
|
+
gameId: data.gameId
|
|
59045
|
+
});
|
|
59005
59046
|
const event = {
|
|
59006
59047
|
"@context": CALIPER_CONSTANTS4.context,
|
|
59007
59048
|
id: `urn:uuid:${crypto.randomUUID()}`,
|
|
@@ -59042,7 +59083,8 @@ function createCaliperNamespace(client) {
|
|
|
59042
59083
|
...data.wasteTimeSeconds !== undefined ? [{ type: TIME_METRIC_TYPES4.waste, value: data.wasteTimeSeconds }] : []
|
|
59043
59084
|
],
|
|
59044
59085
|
...data.extensions ? { extensions: data.extensions } : {}
|
|
59045
|
-
}
|
|
59086
|
+
},
|
|
59087
|
+
...eventExtensions ? { extensions: eventExtensions } : {}
|
|
59046
59088
|
};
|
|
59047
59089
|
return caliper.emit(event, data.sensorUrl);
|
|
59048
59090
|
},
|
|
@@ -59535,6 +59577,7 @@ class AdminEventRecorder {
|
|
|
59535
59577
|
await this.caliper.emitActivityEvent({
|
|
59536
59578
|
studentId: ctx.student.id,
|
|
59537
59579
|
studentEmail: ctx.student.email,
|
|
59580
|
+
gameId: data.gameId,
|
|
59538
59581
|
activityId: ctx.activityId,
|
|
59539
59582
|
activityName: data.activityName || `Manual XP Assignment - ${ctx.courseContext.subject}`,
|
|
59540
59583
|
courseId: data.courseId,
|
|
@@ -59561,6 +59604,7 @@ class AdminEventRecorder {
|
|
|
59561
59604
|
await this.caliper.emitTimeSpentEvent({
|
|
59562
59605
|
studentId: ctx.student.id,
|
|
59563
59606
|
studentEmail: ctx.student.email,
|
|
59607
|
+
gameId: data.gameId,
|
|
59564
59608
|
activityId: ctx.activityId,
|
|
59565
59609
|
activityName: data.activityName || "Playcademy Admin Time Adjustment",
|
|
59566
59610
|
courseId: data.courseId,
|
|
@@ -59582,6 +59626,7 @@ class AdminEventRecorder {
|
|
|
59582
59626
|
await this.caliper.emitActivityEvent({
|
|
59583
59627
|
studentId: ctx.student.id,
|
|
59584
59628
|
studentEmail: ctx.student.email,
|
|
59629
|
+
gameId: data.gameId,
|
|
59585
59630
|
activityId: ctx.activityId,
|
|
59586
59631
|
activityName: data.activityName || "Playcademy Admin Mastery Adjustment",
|
|
59587
59632
|
courseId: data.courseId,
|
|
@@ -59607,6 +59652,7 @@ class AdminEventRecorder {
|
|
|
59607
59652
|
await this.caliper.emitActivityEvent({
|
|
59608
59653
|
studentId: ctx.student.id,
|
|
59609
59654
|
studentEmail: ctx.student.email,
|
|
59655
|
+
gameId: data.gameId,
|
|
59610
59656
|
activityId: ctx.activityId,
|
|
59611
59657
|
activityName: isResume ? "Course resumed" : "Course marked complete",
|
|
59612
59658
|
courseId: data.courseId,
|
|
@@ -60055,15 +60101,13 @@ class ProgressRecorder {
|
|
|
60055
60101
|
studentId,
|
|
60056
60102
|
attemptNumber: currentAttemptNumber,
|
|
60057
60103
|
score,
|
|
60058
|
-
totalQuestions,
|
|
60059
|
-
correctQuestions,
|
|
60060
60104
|
xp: calculatedXp,
|
|
60061
|
-
masteredUnits,
|
|
60062
60105
|
scoreStatus,
|
|
60063
60106
|
inProgress,
|
|
60064
60107
|
appName: progressData.appName,
|
|
60065
|
-
|
|
60066
|
-
|
|
60108
|
+
totalQuestions,
|
|
60109
|
+
correctQuestions,
|
|
60110
|
+
masteredUnits
|
|
60067
60111
|
});
|
|
60068
60112
|
} else {
|
|
60069
60113
|
log.warn("[ProgressRecorder] Score not provided, skipping gradebook entry", {
|
|
@@ -60077,6 +60121,7 @@ class ProgressRecorder {
|
|
|
60077
60121
|
await this.emitCourseCompletionHistoryEvent({
|
|
60078
60122
|
studentId,
|
|
60079
60123
|
studentEmail,
|
|
60124
|
+
gameId: progressData.gameId,
|
|
60080
60125
|
activityId,
|
|
60081
60126
|
courseId: ids.course,
|
|
60082
60127
|
courseName,
|
|
@@ -60088,6 +60133,7 @@ class ProgressRecorder {
|
|
|
60088
60133
|
await this.emitCaliperEvent({
|
|
60089
60134
|
studentId,
|
|
60090
60135
|
studentEmail,
|
|
60136
|
+
gameId: progressData.gameId,
|
|
60091
60137
|
activityId,
|
|
60092
60138
|
activityName,
|
|
60093
60139
|
courseId: ids.course,
|
|
@@ -60189,15 +60235,13 @@ class ProgressRecorder {
|
|
|
60189
60235
|
studentId,
|
|
60190
60236
|
attemptNumber,
|
|
60191
60237
|
score,
|
|
60192
|
-
totalQuestions,
|
|
60193
|
-
correctQuestions,
|
|
60194
60238
|
xp,
|
|
60195
|
-
masteredUnits,
|
|
60196
60239
|
scoreStatus,
|
|
60197
60240
|
inProgress,
|
|
60198
60241
|
appName,
|
|
60199
|
-
|
|
60200
|
-
|
|
60242
|
+
totalQuestions,
|
|
60243
|
+
correctQuestions,
|
|
60244
|
+
masteredUnits
|
|
60201
60245
|
}) {
|
|
60202
60246
|
const timestamp3 = Date.now().toString(36);
|
|
60203
60247
|
const resultId = `${lineItemId}:${studentId}:${timestamp3}`;
|
|
@@ -60212,21 +60256,18 @@ class ProgressRecorder {
|
|
|
60212
60256
|
inProgress,
|
|
60213
60257
|
metadata: {
|
|
60214
60258
|
xp,
|
|
60215
|
-
totalQuestions,
|
|
60216
|
-
correctQuestions,
|
|
60217
|
-
accuracy: totalQuestions && correctQuestions ? correctQuestions / totalQuestions * 100 : undefined,
|
|
60218
60259
|
attemptNumber,
|
|
60219
|
-
lastUpdated: new Date().toISOString(),
|
|
60220
|
-
masteredUnits,
|
|
60221
60260
|
appName,
|
|
60222
|
-
|
|
60223
|
-
|
|
60261
|
+
...totalQuestions !== undefined ? { totalQuestions } : {},
|
|
60262
|
+
...correctQuestions !== undefined ? { correctQuestions } : {},
|
|
60263
|
+
...masteredUnits !== undefined ? { masteredUnits } : {}
|
|
60224
60264
|
}
|
|
60225
60265
|
});
|
|
60226
60266
|
}
|
|
60227
60267
|
async emitCaliperEvent({
|
|
60228
60268
|
studentId,
|
|
60229
60269
|
studentEmail,
|
|
60270
|
+
gameId,
|
|
60230
60271
|
activityId,
|
|
60231
60272
|
activityName,
|
|
60232
60273
|
courseId,
|
|
@@ -60243,6 +60284,7 @@ class ProgressRecorder {
|
|
|
60243
60284
|
await this.caliperNamespace.emitActivityEvent({
|
|
60244
60285
|
studentId,
|
|
60245
60286
|
studentEmail,
|
|
60287
|
+
gameId,
|
|
60246
60288
|
activityId,
|
|
60247
60289
|
activityName,
|
|
60248
60290
|
courseId,
|
|
@@ -60265,6 +60307,7 @@ class ProgressRecorder {
|
|
|
60265
60307
|
await this.caliperNamespace.emitActivityEvent({
|
|
60266
60308
|
studentId: data.studentId,
|
|
60267
60309
|
studentEmail: data.studentEmail,
|
|
60310
|
+
gameId: data.gameId,
|
|
60268
60311
|
activityId: data.activityId,
|
|
60269
60312
|
activityName: "Course completed",
|
|
60270
60313
|
courseId: data.courseId,
|
|
@@ -60314,6 +60357,7 @@ class SessionRecorder {
|
|
|
60314
60357
|
await this.caliperNamespace.emitTimeSpentEvent({
|
|
60315
60358
|
studentId,
|
|
60316
60359
|
studentEmail,
|
|
60360
|
+
gameId: sessionData.gameId,
|
|
60317
60361
|
activityId,
|
|
60318
60362
|
activityName,
|
|
60319
60363
|
courseId: ids.course,
|
|
@@ -118902,18 +118946,23 @@ async function seedCoreGames(db2) {
|
|
|
118902
118946
|
}
|
|
118903
118947
|
async function seedCurrentProjectGame(db2, project) {
|
|
118904
118948
|
const now2 = new Date;
|
|
118949
|
+
const desiredGameId = project.gameId?.trim() || undefined;
|
|
118905
118950
|
try {
|
|
118906
118951
|
const existingGame = await db2.query.games.findFirst({
|
|
118907
|
-
where: (row,
|
|
118952
|
+
where: (row, operators) => operators.eq(row.slug, project.slug)
|
|
118908
118953
|
});
|
|
118909
118954
|
if (existingGame) {
|
|
118910
|
-
if (
|
|
118911
|
-
await
|
|
118955
|
+
if (desiredGameId && existingGame.id !== desiredGameId) {
|
|
118956
|
+
await db2.delete(games).where(eq(games.id, existingGame.id));
|
|
118957
|
+
} else {
|
|
118958
|
+
if (project.timebackCourses && project.timebackCourses.length > 0) {
|
|
118959
|
+
await seedTimebackIntegrations(db2, existingGame.id, project.timebackCourses);
|
|
118960
|
+
}
|
|
118961
|
+
return existingGame;
|
|
118912
118962
|
}
|
|
118913
|
-
return existingGame;
|
|
118914
118963
|
}
|
|
118915
118964
|
const gameRecord = {
|
|
118916
|
-
id: crypto.randomUUID(),
|
|
118965
|
+
id: desiredGameId ?? crypto.randomUUID(),
|
|
118917
118966
|
developerId: DEMO_USERS.developer.id,
|
|
118918
118967
|
slug: project.slug,
|
|
118919
118968
|
displayName: project.displayName,
|
|
@@ -118942,6 +118991,7 @@ async function seedCurrentProjectGame(db2, project) {
|
|
|
118942
118991
|
}
|
|
118943
118992
|
}
|
|
118944
118993
|
var init_games = __esm(() => {
|
|
118994
|
+
init_drizzle_orm();
|
|
118945
118995
|
init_src();
|
|
118946
118996
|
init_tables_index();
|
|
118947
118997
|
init_constants();
|
|
@@ -120340,6 +120390,7 @@ var init_schemas11 = __esm(() => {
|
|
|
120340
120390
|
gameId: exports_external.string().uuid(),
|
|
120341
120391
|
studentId: exports_external.string().min(1),
|
|
120342
120392
|
runId: exports_external.string().uuid().optional(),
|
|
120393
|
+
resumeId: exports_external.string().uuid(),
|
|
120343
120394
|
activityData: TimebackActivityDataSchema,
|
|
120344
120395
|
scoreData: exports_external.object({
|
|
120345
120396
|
correctQuestions: exports_external.number().int().min(0),
|
|
@@ -120360,12 +120411,13 @@ var init_schemas11 = __esm(() => {
|
|
|
120360
120411
|
gameId: exports_external.string().uuid(),
|
|
120361
120412
|
studentId: exports_external.string().min(1),
|
|
120362
120413
|
runId: exports_external.string().uuid(),
|
|
120414
|
+
resumeId: exports_external.string().uuid(),
|
|
120363
120415
|
activityData: TimebackActivityDataSchema,
|
|
120364
120416
|
timingData: exports_external.object({
|
|
120365
120417
|
activeMs: exports_external.number().nonnegative(),
|
|
120366
120418
|
pausedMs: exports_external.number().nonnegative()
|
|
120367
120419
|
}),
|
|
120368
|
-
|
|
120420
|
+
windowStartedAtMs: exports_external.number().int().nonnegative(),
|
|
120369
120421
|
isFinal: exports_external.boolean().optional()
|
|
120370
120422
|
});
|
|
120371
120423
|
PopulateStudentRequestSchema = exports_external.object({
|
|
@@ -120447,15 +120499,18 @@ var init_schemas11 = __esm(() => {
|
|
|
120447
120499
|
});
|
|
120448
120500
|
GrantTimebackXpRequestSchema = AdminTimebackMutationBaseSchema.extend({
|
|
120449
120501
|
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()
|
|
120502
|
+
date: AdminAttributionDateSchema.optional(),
|
|
120503
|
+
useCurrentTime: exports_external.boolean().optional()
|
|
120451
120504
|
});
|
|
120452
120505
|
AdjustTimebackTimeRequestSchema = AdminTimebackMutationBaseSchema.extend({
|
|
120453
120506
|
seconds: exports_external.number().refine((value) => value !== 0, { message: "Time amount cannot be 0" }),
|
|
120454
|
-
date: AdminAttributionDateSchema.optional()
|
|
120507
|
+
date: AdminAttributionDateSchema.optional(),
|
|
120508
|
+
useCurrentTime: exports_external.boolean().optional()
|
|
120455
120509
|
});
|
|
120456
120510
|
AdjustTimebackMasteryRequestSchema = AdminTimebackMutationBaseSchema.extend({
|
|
120457
120511
|
units: exports_external.number().refine((value) => value !== 0, { message: "Units cannot be 0" }),
|
|
120458
|
-
date: AdminAttributionDateSchema.optional()
|
|
120512
|
+
date: AdminAttributionDateSchema.optional(),
|
|
120513
|
+
useCurrentTime: exports_external.boolean().optional()
|
|
120459
120514
|
});
|
|
120460
120515
|
ToggleCourseCompletionRequestSchema = exports_external.object({
|
|
120461
120516
|
gameId: exports_external.string().uuid(),
|
|
@@ -122836,6 +122891,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
122836
122891
|
gameId,
|
|
122837
122892
|
studentId,
|
|
122838
122893
|
runId,
|
|
122894
|
+
resumeId,
|
|
122839
122895
|
activityData,
|
|
122840
122896
|
scoreData,
|
|
122841
122897
|
timingData,
|
|
@@ -122849,6 +122905,7 @@ var init_timeback_controller = __esm(() => {
|
|
|
122849
122905
|
gameId,
|
|
122850
122906
|
studentId,
|
|
122851
122907
|
runId,
|
|
122908
|
+
resumeId,
|
|
122852
122909
|
activityData,
|
|
122853
122910
|
scoreData,
|
|
122854
122911
|
timingData,
|
|
@@ -122872,12 +122929,22 @@ var init_timeback_controller = __esm(() => {
|
|
|
122872
122929
|
}
|
|
122873
122930
|
throw ApiError.badRequest("Invalid JSON body");
|
|
122874
122931
|
}
|
|
122875
|
-
const {
|
|
122932
|
+
const {
|
|
122933
|
+
gameId,
|
|
122934
|
+
studentId,
|
|
122935
|
+
runId,
|
|
122936
|
+
resumeId,
|
|
122937
|
+
activityData,
|
|
122938
|
+
timingData,
|
|
122939
|
+
windowStartedAtMs,
|
|
122940
|
+
isFinal
|
|
122941
|
+
} = body2;
|
|
122876
122942
|
logger63.debug("Recording heartbeat", {
|
|
122877
122943
|
userId: ctx.user.id,
|
|
122878
122944
|
gameId,
|
|
122879
122945
|
runId,
|
|
122880
|
-
|
|
122946
|
+
resumeId,
|
|
122947
|
+
windowStartedAtMs,
|
|
122881
122948
|
activeMs: timingData.activeMs,
|
|
122882
122949
|
isFinal
|
|
122883
122950
|
});
|
|
@@ -122885,9 +122952,10 @@ var init_timeback_controller = __esm(() => {
|
|
|
122885
122952
|
gameId,
|
|
122886
122953
|
studentId,
|
|
122887
122954
|
runId,
|
|
122955
|
+
resumeId,
|
|
122888
122956
|
activityData,
|
|
122889
122957
|
timingData,
|
|
122890
|
-
|
|
122958
|
+
windowStartedAtMs,
|
|
122891
122959
|
isFinal,
|
|
122892
122960
|
user: ctx.user
|
|
122893
122961
|
});
|
|
@@ -124471,6 +124539,203 @@ function printBanner(viteConfig, options) {
|
|
|
124471
124539
|
import fs5 from "node:fs";
|
|
124472
124540
|
import path3 from "node:path";
|
|
124473
124541
|
import { loadPlaycademyConfig } from "playcademy/utils";
|
|
124542
|
+
|
|
124543
|
+
// ../utils/src/uuid.ts
|
|
124544
|
+
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}$/;
|
|
124545
|
+
function isValidUUID2(value) {
|
|
124546
|
+
if (!value || typeof value !== "string") {
|
|
124547
|
+
return false;
|
|
124548
|
+
}
|
|
124549
|
+
return UUID_REGEX2.test(value);
|
|
124550
|
+
}
|
|
124551
|
+
// ../utils/src/ansi.ts
|
|
124552
|
+
var colors3 = {
|
|
124553
|
+
black: "\x1B[30m",
|
|
124554
|
+
red: "\x1B[31m",
|
|
124555
|
+
green: "\x1B[32m",
|
|
124556
|
+
yellow: "\x1B[33m",
|
|
124557
|
+
blue: "\x1B[34m",
|
|
124558
|
+
magenta: "\x1B[35m",
|
|
124559
|
+
cyan: "\x1B[36m",
|
|
124560
|
+
white: "\x1B[37m",
|
|
124561
|
+
gray: "\x1B[90m"
|
|
124562
|
+
};
|
|
124563
|
+
var styles3 = {
|
|
124564
|
+
reset: "\x1B[0m",
|
|
124565
|
+
bold: "\x1B[1m",
|
|
124566
|
+
dim: "\x1B[2m",
|
|
124567
|
+
italic: "\x1B[3m",
|
|
124568
|
+
underline: "\x1B[4m"
|
|
124569
|
+
};
|
|
124570
|
+
var cursor2 = {
|
|
124571
|
+
hide: "\x1B[?25l",
|
|
124572
|
+
show: "\x1B[?25h",
|
|
124573
|
+
up: (n3) => `\x1B[${n3}A`,
|
|
124574
|
+
down: (n3) => `\x1B[${n3}B`,
|
|
124575
|
+
forward: (n3) => `\x1B[${n3}C`,
|
|
124576
|
+
back: (n3) => `\x1B[${n3}D`,
|
|
124577
|
+
clearLine: "\x1B[K",
|
|
124578
|
+
clearScreen: "\x1B[2J",
|
|
124579
|
+
home: "\x1B[H"
|
|
124580
|
+
};
|
|
124581
|
+
var isInteractive2 = typeof process !== "undefined" && process.stdout?.isTTY && !process.env.CI && process.env.TERM !== "dumb";
|
|
124582
|
+
function stripAnsi2(text2) {
|
|
124583
|
+
return text2.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
|
|
124584
|
+
}
|
|
124585
|
+
|
|
124586
|
+
// ../utils/src/spinner.ts
|
|
124587
|
+
import { stdout as stdout2 } from "process";
|
|
124588
|
+
var SPINNER_FRAMES2 = [
|
|
124589
|
+
10251,
|
|
124590
|
+
10265,
|
|
124591
|
+
10297,
|
|
124592
|
+
10296,
|
|
124593
|
+
10300,
|
|
124594
|
+
10292,
|
|
124595
|
+
10278,
|
|
124596
|
+
10279,
|
|
124597
|
+
10247,
|
|
124598
|
+
10255
|
|
124599
|
+
].map((code) => String.fromCodePoint(code));
|
|
124600
|
+
var CHECK_MARK2 = String.fromCodePoint(10004);
|
|
124601
|
+
var CROSS_MARK2 = String.fromCodePoint(10006);
|
|
124602
|
+
var CANCEL_MARK2 = String.fromCodePoint(9675);
|
|
124603
|
+
var SPINNER_INTERVAL2 = 80;
|
|
124604
|
+
|
|
124605
|
+
class Spinner3 {
|
|
124606
|
+
tasks = new Map;
|
|
124607
|
+
frameIndex = 0;
|
|
124608
|
+
intervalId = null;
|
|
124609
|
+
renderCount = 0;
|
|
124610
|
+
previousLineCount = 0;
|
|
124611
|
+
printedTasks = new Set;
|
|
124612
|
+
indent;
|
|
124613
|
+
constructor(taskIds, texts, options) {
|
|
124614
|
+
this.indent = options?.indent ?? 0;
|
|
124615
|
+
taskIds.forEach((id, index6) => {
|
|
124616
|
+
this.tasks.set(id, {
|
|
124617
|
+
text: texts[index6] || "",
|
|
124618
|
+
status: "pending"
|
|
124619
|
+
});
|
|
124620
|
+
});
|
|
124621
|
+
}
|
|
124622
|
+
start() {
|
|
124623
|
+
if (isInteractive2) {
|
|
124624
|
+
stdout2.write(cursor2.hide);
|
|
124625
|
+
this.render();
|
|
124626
|
+
this.intervalId = setInterval(() => {
|
|
124627
|
+
this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES2.length;
|
|
124628
|
+
this.render();
|
|
124629
|
+
}, SPINNER_INTERVAL2);
|
|
124630
|
+
}
|
|
124631
|
+
}
|
|
124632
|
+
clear() {
|
|
124633
|
+
if (this.intervalId) {
|
|
124634
|
+
clearInterval(this.intervalId);
|
|
124635
|
+
this.intervalId = null;
|
|
124636
|
+
}
|
|
124637
|
+
if (isInteractive2 && this.previousLineCount > 0) {
|
|
124638
|
+
stdout2.write(cursor2.up(this.previousLineCount));
|
|
124639
|
+
for (let i3 = 0;i3 < this.previousLineCount; i3++) {
|
|
124640
|
+
stdout2.write(`\r${cursor2.clearLine}
|
|
124641
|
+
`);
|
|
124642
|
+
}
|
|
124643
|
+
stdout2.write(cursor2.up(this.previousLineCount));
|
|
124644
|
+
stdout2.write(cursor2.show);
|
|
124645
|
+
}
|
|
124646
|
+
this.previousLineCount = 0;
|
|
124647
|
+
}
|
|
124648
|
+
updateTask(taskId, status, finalText) {
|
|
124649
|
+
const task = this.tasks.get(taskId);
|
|
124650
|
+
if (task) {
|
|
124651
|
+
task.status = status;
|
|
124652
|
+
if (finalText) {
|
|
124653
|
+
task.finalText = finalText;
|
|
124654
|
+
}
|
|
124655
|
+
if (!isInteractive2) {
|
|
124656
|
+
this.renderNonInteractive(taskId, task);
|
|
124657
|
+
}
|
|
124658
|
+
}
|
|
124659
|
+
}
|
|
124660
|
+
renderNonInteractive(taskId, task) {
|
|
124661
|
+
const key = `${taskId}-${task.status}`;
|
|
124662
|
+
if (this.printedTasks.has(key)) {
|
|
124663
|
+
return;
|
|
124664
|
+
}
|
|
124665
|
+
this.printedTasks.add(key);
|
|
124666
|
+
const indentStr = " ".repeat(this.indent);
|
|
124667
|
+
let line2 = "";
|
|
124668
|
+
switch (task.status) {
|
|
124669
|
+
case "running": {
|
|
124670
|
+
line2 = `${indentStr}[RUNNING] ${stripAnsi2(task.text)}`;
|
|
124671
|
+
break;
|
|
124672
|
+
}
|
|
124673
|
+
case "success": {
|
|
124674
|
+
line2 = `${indentStr}[SUCCESS] ${stripAnsi2(task.finalText || task.text)}`;
|
|
124675
|
+
break;
|
|
124676
|
+
}
|
|
124677
|
+
case "error": {
|
|
124678
|
+
line2 = `${indentStr}[ERROR] Failed: ${stripAnsi2(task.text)}`;
|
|
124679
|
+
break;
|
|
124680
|
+
}
|
|
124681
|
+
case "cancelled": {
|
|
124682
|
+
line2 = `${indentStr}[CANCELLED] ${stripAnsi2(task.finalText || task.text)}`;
|
|
124683
|
+
break;
|
|
124684
|
+
}
|
|
124685
|
+
}
|
|
124686
|
+
console.log(line2);
|
|
124687
|
+
}
|
|
124688
|
+
render() {
|
|
124689
|
+
if (this.previousLineCount > 0) {
|
|
124690
|
+
stdout2.write(cursor2.up(this.previousLineCount));
|
|
124691
|
+
}
|
|
124692
|
+
const spinner = SPINNER_FRAMES2[this.frameIndex];
|
|
124693
|
+
const indentStr = " ".repeat(this.indent);
|
|
124694
|
+
const visibleTasks = [...this.tasks.values()].filter((task) => task.status !== "pending");
|
|
124695
|
+
for (const task of visibleTasks) {
|
|
124696
|
+
stdout2.write(`\r${cursor2.clearLine}`);
|
|
124697
|
+
let line2 = "";
|
|
124698
|
+
switch (task.status) {
|
|
124699
|
+
case "running": {
|
|
124700
|
+
line2 = `${indentStr}${colors3.blue}${spinner}${styles3.reset} ${task.text}`;
|
|
124701
|
+
break;
|
|
124702
|
+
}
|
|
124703
|
+
case "success": {
|
|
124704
|
+
line2 = `${indentStr}${colors3.green}${CHECK_MARK2}${styles3.reset} ${task.finalText || task.text}`;
|
|
124705
|
+
break;
|
|
124706
|
+
}
|
|
124707
|
+
case "error": {
|
|
124708
|
+
line2 = `${indentStr}${colors3.red}${CROSS_MARK2}${styles3.reset} Failed: ${task.text}`;
|
|
124709
|
+
break;
|
|
124710
|
+
}
|
|
124711
|
+
case "cancelled": {
|
|
124712
|
+
line2 = `${indentStr}${colors3.gray}${CANCEL_MARK2}${styles3.reset} Cancelled: ${task.finalText || task.text}`;
|
|
124713
|
+
break;
|
|
124714
|
+
}
|
|
124715
|
+
}
|
|
124716
|
+
console.log(line2);
|
|
124717
|
+
}
|
|
124718
|
+
this.previousLineCount = visibleTasks.length;
|
|
124719
|
+
this.renderCount++;
|
|
124720
|
+
}
|
|
124721
|
+
stop() {
|
|
124722
|
+
if (this.intervalId) {
|
|
124723
|
+
clearInterval(this.intervalId);
|
|
124724
|
+
this.intervalId = null;
|
|
124725
|
+
}
|
|
124726
|
+
if (isInteractive2) {
|
|
124727
|
+
this.render();
|
|
124728
|
+
stdout2.write(cursor2.show);
|
|
124729
|
+
} else {
|
|
124730
|
+
this.tasks.forEach((task, taskId) => {
|
|
124731
|
+
if (task.status !== "pending") {
|
|
124732
|
+
this.renderNonInteractive(taskId, task);
|
|
124733
|
+
}
|
|
124734
|
+
});
|
|
124735
|
+
}
|
|
124736
|
+
}
|
|
124737
|
+
}
|
|
124738
|
+
// src/lib/sandbox/project-info.ts
|
|
124474
124739
|
function extractTimebackCourses(config2, timebackOptions) {
|
|
124475
124740
|
const courses = config2?.integrations?.timeback?.courses;
|
|
124476
124741
|
if (!courses || courses.length === 0) {
|
|
@@ -124504,17 +124769,20 @@ async function extractProjectInfo(viteConfig, timebackOptions) {
|
|
|
124504
124769
|
packageJson = JSON.parse(packageJsonContent);
|
|
124505
124770
|
}
|
|
124506
124771
|
} catch {}
|
|
124507
|
-
const
|
|
124508
|
-
let
|
|
124509
|
-
if (
|
|
124510
|
-
|
|
124772
|
+
const name4 = config2?.name || packageJson.name || "";
|
|
124773
|
+
let slug2 = name4;
|
|
124774
|
+
if (slug2.includes("/")) {
|
|
124775
|
+
slug2 = slug2.split("/")[1] || slug2;
|
|
124511
124776
|
}
|
|
124512
|
-
if (!
|
|
124513
|
-
|
|
124777
|
+
if (!slug2) {
|
|
124778
|
+
slug2 = directoryName;
|
|
124514
124779
|
}
|
|
124515
|
-
const displayName =
|
|
124780
|
+
const displayName = slug2.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
124781
|
+
const envGameId = process.env.SANDBOX_GAME_ID;
|
|
124782
|
+
const gameId = envGameId && isValidUUID2(envGameId) ? envGameId : undefined;
|
|
124516
124783
|
return {
|
|
124517
|
-
|
|
124784
|
+
gameId,
|
|
124785
|
+
slug: slug2,
|
|
124518
124786
|
displayName,
|
|
124519
124787
|
version: packageJson.version || "dev",
|
|
124520
124788
|
description: packageJson.description,
|
|
@@ -125549,7 +125817,7 @@ var DEBOUNCE_MS = 500;
|
|
|
125549
125817
|
var VITE_CONFIG_NAMES = ["vite.config.ts", "vite.config.js", "vite.config.mjs"];
|
|
125550
125818
|
var debounceTimer = null;
|
|
125551
125819
|
function findExistingFiles(projectRoot, fileNames) {
|
|
125552
|
-
return fileNames.map((
|
|
125820
|
+
return fileNames.map((name4) => path4.join(projectRoot, name4)).filter((file) => fs7.existsSync(file));
|
|
125553
125821
|
}
|
|
125554
125822
|
function createChangeHandler(server, viteConfig, platformModeOptions, watchedFiles) {
|
|
125555
125823
|
return async (changedPath) => {
|
|
@@ -125617,7 +125885,7 @@ function cyclePlatformRoleHotkey(options) {
|
|
|
125617
125885
|
|
|
125618
125886
|
// src/server/hotkeys/cycle-timeback-role.ts
|
|
125619
125887
|
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
125620
|
-
var { bold:
|
|
125888
|
+
var { bold: bold5, cyan: cyan4, green: green4, red: red3, yellow: yellow3 } = import_picocolors9.default;
|
|
125621
125889
|
function cycleTimebackRole(logger) {
|
|
125622
125890
|
const currentRole = getTimebackRoleOverride() ?? "student";
|
|
125623
125891
|
const currentIndex = TIMEBACK_ROLES.indexOf(currentRole);
|
|
@@ -125636,14 +125904,14 @@ function cycleTimebackRole(logger) {
|
|
|
125636
125904
|
function cycleTimebackRoleHotkey(options) {
|
|
125637
125905
|
return {
|
|
125638
125906
|
key: "t",
|
|
125639
|
-
description: `${cyan4(
|
|
125907
|
+
description: `${cyan4(bold5("[playcademy]"))} cycle Timeback role`,
|
|
125640
125908
|
action: () => cycleTimebackRole(options.viteConfig.logger)
|
|
125641
125909
|
};
|
|
125642
125910
|
}
|
|
125643
125911
|
|
|
125644
125912
|
// src/server/hotkeys/recreate-database.ts
|
|
125645
125913
|
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
125646
|
-
var { bold:
|
|
125914
|
+
var { bold: bold6, cyan: cyan5 } = import_picocolors10.default;
|
|
125647
125915
|
async function recreateSandboxDatabase(options) {
|
|
125648
125916
|
await recreateSandbox({
|
|
125649
125917
|
viteConfig: options.viteConfig,
|
|
@@ -125653,7 +125921,7 @@ async function recreateSandboxDatabase(options) {
|
|
|
125653
125921
|
function recreateDatabaseHotkey(options) {
|
|
125654
125922
|
return {
|
|
125655
125923
|
key: "d",
|
|
125656
|
-
description: `${cyan5(
|
|
125924
|
+
description: `${cyan5(bold6("[playcademy]"))} recreate sandbox database`,
|
|
125657
125925
|
action: () => recreateSandboxDatabase(options)
|
|
125658
125926
|
};
|
|
125659
125927
|
}
|
|
@@ -125663,7 +125931,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
|
125663
125931
|
// package.json
|
|
125664
125932
|
var package_default2 = {
|
|
125665
125933
|
name: "@playcademy/vite-plugin",
|
|
125666
|
-
version: "0.2.24-beta.
|
|
125934
|
+
version: "0.2.24-beta.7",
|
|
125667
125935
|
type: "module",
|
|
125668
125936
|
exports: {
|
|
125669
125937
|
".": {
|
package/dist/types/internal.d.ts
CHANGED