@playcademy/sandbox 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +237 -17
- package/dist/server.d.ts +11 -0
- package/dist/server.js +51 -13
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -135703,7 +135703,7 @@ var serve = (options, listeningListener) => {
|
|
|
135703
135703
|
// package.json
|
|
135704
135704
|
var package_default = {
|
|
135705
135705
|
name: "@playcademy/sandbox",
|
|
135706
|
-
version: "0.2.
|
|
135706
|
+
version: "0.2.2",
|
|
135707
135707
|
description: "Local development server for Playcademy game development",
|
|
135708
135708
|
type: "module",
|
|
135709
135709
|
exports: {
|
|
@@ -159749,6 +159749,47 @@ var logger3 = {
|
|
|
159749
159749
|
warn: (msg) => getLogger().warn(msg),
|
|
159750
159750
|
error: (msg) => getLogger().error(msg)
|
|
159751
159751
|
};
|
|
159752
|
+
// src/database/seed/timeback.ts
|
|
159753
|
+
function resolveStudentId(studentId) {
|
|
159754
|
+
if (!studentId)
|
|
159755
|
+
return null;
|
|
159756
|
+
if (studentId === "mock")
|
|
159757
|
+
return `mock-student-${crypto.randomUUID().slice(0, 8)}`;
|
|
159758
|
+
return studentId;
|
|
159759
|
+
}
|
|
159760
|
+
function getAdminTimebackId() {
|
|
159761
|
+
return resolveStudentId(config.timeback.studentId);
|
|
159762
|
+
}
|
|
159763
|
+
async function seedTimebackIntegrations(db, gameId, courses) {
|
|
159764
|
+
const now2 = new Date;
|
|
159765
|
+
let seededCount = 0;
|
|
159766
|
+
for (const course of courses) {
|
|
159767
|
+
const courseId = `mock-${course.subject.toLowerCase()}-g${course.grade}`;
|
|
159768
|
+
try {
|
|
159769
|
+
const existing = await db.query.gameTimebackIntegrations.findFirst({
|
|
159770
|
+
where: (table14, { and: and3, eq: eq3 }) => and3(eq3(table14.gameId, gameId), eq3(table14.grade, course.grade), eq3(table14.subject, course.subject))
|
|
159771
|
+
});
|
|
159772
|
+
if (existing) {
|
|
159773
|
+
continue;
|
|
159774
|
+
}
|
|
159775
|
+
await db.insert(gameTimebackIntegrations).values({
|
|
159776
|
+
gameId,
|
|
159777
|
+
courseId,
|
|
159778
|
+
grade: course.grade,
|
|
159779
|
+
subject: course.subject,
|
|
159780
|
+
totalXp: course.totalXp ?? 1000,
|
|
159781
|
+
lastVerifiedAt: now2
|
|
159782
|
+
});
|
|
159783
|
+
seededCount++;
|
|
159784
|
+
} catch (error2) {
|
|
159785
|
+
console.error(`❌ Error seeding TimeBack integration for ${course.subject}:${course.grade}:`, error2);
|
|
159786
|
+
}
|
|
159787
|
+
}
|
|
159788
|
+
if (seededCount > 0) {
|
|
159789
|
+
logger3.info(`\uD83D\uDCDA Seeded ${seededCount} TimeBack integration(s)`);
|
|
159790
|
+
}
|
|
159791
|
+
}
|
|
159792
|
+
|
|
159752
159793
|
// src/database/seed/games.ts
|
|
159753
159794
|
async function seedCoreGames(db) {
|
|
159754
159795
|
const now2 = new Date;
|
|
@@ -159783,6 +159824,9 @@ async function seedCurrentProjectGame(db, project) {
|
|
|
159783
159824
|
});
|
|
159784
159825
|
if (existingGame) {
|
|
159785
159826
|
logger3.info(`\uD83C\uDFAE Game "${project.displayName}" (${project.slug}) already exists`);
|
|
159827
|
+
if (project.timebackCourses && project.timebackCourses.length > 0) {
|
|
159828
|
+
await seedTimebackIntegrations(db, existingGame.id, project.timebackCourses);
|
|
159829
|
+
}
|
|
159786
159830
|
return existingGame;
|
|
159787
159831
|
}
|
|
159788
159832
|
const gameRecord = {
|
|
@@ -159802,6 +159846,12 @@ async function seedCurrentProjectGame(db, project) {
|
|
|
159802
159846
|
updatedAt: now2
|
|
159803
159847
|
};
|
|
159804
159848
|
const [newGame] = await db.insert(games).values(gameRecord).returning();
|
|
159849
|
+
if (!newGame) {
|
|
159850
|
+
throw new Error("Failed to create game record");
|
|
159851
|
+
}
|
|
159852
|
+
if (project.timebackCourses && project.timebackCourses.length > 0) {
|
|
159853
|
+
await seedTimebackIntegrations(db, newGame.id, project.timebackCourses);
|
|
159854
|
+
}
|
|
159805
159855
|
return newGame;
|
|
159806
159856
|
} catch (error2) {
|
|
159807
159857
|
console.error("❌ Error seeding project game:", error2);
|
|
@@ -159863,18 +159913,6 @@ async function seedSpriteTemplates(db) {
|
|
|
159863
159913
|
}
|
|
159864
159914
|
}
|
|
159865
159915
|
|
|
159866
|
-
// src/database/seed/timeback.ts
|
|
159867
|
-
function resolveStudentId(studentId) {
|
|
159868
|
-
if (!studentId)
|
|
159869
|
-
return null;
|
|
159870
|
-
if (studentId === "mock")
|
|
159871
|
-
return `mock-student-${crypto.randomUUID().slice(0, 8)}`;
|
|
159872
|
-
return studentId;
|
|
159873
|
-
}
|
|
159874
|
-
function getAdminTimebackId() {
|
|
159875
|
-
return resolveStudentId(config.timeback.studentId);
|
|
159876
|
-
}
|
|
159877
|
-
|
|
159878
159916
|
// src/database/seed/index.ts
|
|
159879
159917
|
async function seedDemoData(db) {
|
|
159880
159918
|
try {
|
|
@@ -171450,14 +171488,196 @@ function printBanner(options) {
|
|
|
171450
171488
|
}
|
|
171451
171489
|
|
|
171452
171490
|
// src/cli/options.ts
|
|
171453
|
-
|
|
171491
|
+
import { resolve as resolve3 } from "node:path";
|
|
171492
|
+
|
|
171493
|
+
// ../utils/src/file-loader.ts
|
|
171494
|
+
import { existsSync as existsSync3, readdirSync, statSync as statSync2 } from "fs";
|
|
171495
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
171496
|
+
import { dirname as dirname2, parse as parse2, resolve as resolve2 } from "path";
|
|
171497
|
+
function findFilePath(filename, startDir, maxLevels = 3) {
|
|
171498
|
+
const filenames = Array.isArray(filename) ? filename : [filename];
|
|
171499
|
+
let currentDir = resolve2(startDir);
|
|
171500
|
+
let levelsSearched = 0;
|
|
171501
|
+
while (levelsSearched <= maxLevels) {
|
|
171502
|
+
for (const fname of filenames) {
|
|
171503
|
+
const filePath = resolve2(currentDir, fname);
|
|
171504
|
+
if (existsSync3(filePath)) {
|
|
171505
|
+
return {
|
|
171506
|
+
path: filePath,
|
|
171507
|
+
dir: currentDir,
|
|
171508
|
+
filename: fname
|
|
171509
|
+
};
|
|
171510
|
+
}
|
|
171511
|
+
}
|
|
171512
|
+
if (levelsSearched >= maxLevels) {
|
|
171513
|
+
break;
|
|
171514
|
+
}
|
|
171515
|
+
const parentDir = dirname2(currentDir);
|
|
171516
|
+
if (parentDir === currentDir) {
|
|
171517
|
+
break;
|
|
171518
|
+
}
|
|
171519
|
+
const parsed = parse2(currentDir);
|
|
171520
|
+
if (parsed.root === currentDir) {
|
|
171521
|
+
break;
|
|
171522
|
+
}
|
|
171523
|
+
currentDir = parentDir;
|
|
171524
|
+
levelsSearched++;
|
|
171525
|
+
}
|
|
171526
|
+
return null;
|
|
171527
|
+
}
|
|
171528
|
+
async function loadFile(filename, options = {}) {
|
|
171529
|
+
const {
|
|
171530
|
+
cwd = process.cwd(),
|
|
171531
|
+
required = false,
|
|
171532
|
+
searchUp = false,
|
|
171533
|
+
maxLevels = 3,
|
|
171534
|
+
parseJson = false,
|
|
171535
|
+
stripComments = false
|
|
171536
|
+
} = options;
|
|
171537
|
+
let fileResult;
|
|
171538
|
+
if (searchUp) {
|
|
171539
|
+
fileResult = findFilePath(filename, cwd, maxLevels);
|
|
171540
|
+
} else {
|
|
171541
|
+
const filenames = Array.isArray(filename) ? filename : [filename];
|
|
171542
|
+
fileResult = null;
|
|
171543
|
+
for (const fname of filenames) {
|
|
171544
|
+
const filePath = resolve2(cwd, fname);
|
|
171545
|
+
if (existsSync3(filePath)) {
|
|
171546
|
+
fileResult = {
|
|
171547
|
+
path: filePath,
|
|
171548
|
+
dir: cwd,
|
|
171549
|
+
filename: fname
|
|
171550
|
+
};
|
|
171551
|
+
break;
|
|
171552
|
+
}
|
|
171553
|
+
}
|
|
171554
|
+
}
|
|
171555
|
+
if (!fileResult) {
|
|
171556
|
+
if (required) {
|
|
171557
|
+
const fileList = Array.isArray(filename) ? filename.join(" or ") : filename;
|
|
171558
|
+
const message3 = searchUp ? `${fileList} not found in ${cwd} or up to ${maxLevels} parent directories` : `${fileList} not found at ${cwd}`;
|
|
171559
|
+
throw new Error(message3);
|
|
171560
|
+
}
|
|
171561
|
+
return null;
|
|
171562
|
+
}
|
|
171563
|
+
try {
|
|
171564
|
+
let content = await readFile3(fileResult.path, "utf-8");
|
|
171565
|
+
if (parseJson) {
|
|
171566
|
+
if (stripComments) {
|
|
171567
|
+
content = stripJsonComments(content);
|
|
171568
|
+
}
|
|
171569
|
+
return JSON.parse(content);
|
|
171570
|
+
}
|
|
171571
|
+
return content;
|
|
171572
|
+
} catch (error2) {
|
|
171573
|
+
throw new Error(`Failed to load ${fileResult.filename} from ${fileResult.path}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
171574
|
+
}
|
|
171575
|
+
}
|
|
171576
|
+
function getFileExtension(path3) {
|
|
171577
|
+
return path3.split(".").pop()?.toLowerCase();
|
|
171578
|
+
}
|
|
171579
|
+
function stripJsonComments(jsonc) {
|
|
171580
|
+
let result = jsonc.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
171581
|
+
result = result.replace(/\/\/.*/g, "");
|
|
171582
|
+
return result;
|
|
171583
|
+
}
|
|
171584
|
+
async function loadModule2(filename, options = {}) {
|
|
171585
|
+
const { cwd = process.cwd(), required = false, searchUp = false, maxLevels = 3 } = options;
|
|
171586
|
+
let fileResult;
|
|
171587
|
+
if (searchUp) {
|
|
171588
|
+
fileResult = findFilePath(filename, cwd, maxLevels);
|
|
171589
|
+
} else {
|
|
171590
|
+
const filenames = Array.isArray(filename) ? filename : [filename];
|
|
171591
|
+
fileResult = null;
|
|
171592
|
+
for (const fname of filenames) {
|
|
171593
|
+
const filePath = resolve2(cwd, fname);
|
|
171594
|
+
if (existsSync3(filePath)) {
|
|
171595
|
+
fileResult = {
|
|
171596
|
+
path: filePath,
|
|
171597
|
+
dir: cwd,
|
|
171598
|
+
filename: fname
|
|
171599
|
+
};
|
|
171600
|
+
break;
|
|
171601
|
+
}
|
|
171602
|
+
}
|
|
171603
|
+
}
|
|
171604
|
+
if (!fileResult) {
|
|
171605
|
+
if (required) {
|
|
171606
|
+
const fileList = Array.isArray(filename) ? filename.join(" or ") : filename;
|
|
171607
|
+
const message3 = searchUp ? `${fileList} not found in ${cwd} or up to ${maxLevels} parent directories` : `${fileList} not found at ${cwd}`;
|
|
171608
|
+
throw new Error(message3);
|
|
171609
|
+
}
|
|
171610
|
+
return null;
|
|
171611
|
+
}
|
|
171612
|
+
try {
|
|
171613
|
+
const ext2 = getFileExtension(fileResult.filename);
|
|
171614
|
+
if (ext2 === "js" || ext2 === "mjs" || ext2 === "cjs") {
|
|
171615
|
+
const module2 = await import(fileResult.path);
|
|
171616
|
+
return module2.default || module2;
|
|
171617
|
+
} else {
|
|
171618
|
+
throw new Error(`Unsupported module type: ${ext2} (only .js, .mjs, .cjs supported)`);
|
|
171619
|
+
}
|
|
171620
|
+
} catch (error2) {
|
|
171621
|
+
throw new Error(`Failed to load module ${fileResult.filename} from ${fileResult.path}: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
171622
|
+
}
|
|
171623
|
+
}
|
|
171624
|
+
|
|
171625
|
+
// src/cli/options.ts
|
|
171626
|
+
async function loadPlaycademyConfig(configPath) {
|
|
171627
|
+
try {
|
|
171628
|
+
if (configPath) {
|
|
171629
|
+
const resolved = resolve3(configPath);
|
|
171630
|
+
if (resolved.endsWith(".json")) {
|
|
171631
|
+
return await loadFile(resolved, { parseJson: true });
|
|
171632
|
+
} else {
|
|
171633
|
+
return await loadModule2(resolved);
|
|
171634
|
+
}
|
|
171635
|
+
}
|
|
171636
|
+
const jsonConfig = await loadFile("playcademy.config.json", {
|
|
171637
|
+
parseJson: true
|
|
171638
|
+
});
|
|
171639
|
+
if (jsonConfig)
|
|
171640
|
+
return jsonConfig;
|
|
171641
|
+
const jsConfig = await loadModule2("playcademy.config.js");
|
|
171642
|
+
if (jsConfig)
|
|
171643
|
+
return jsConfig;
|
|
171644
|
+
return null;
|
|
171645
|
+
} catch (error2) {
|
|
171646
|
+
console.warn(`[Sandbox] Failed to load config:`, error2);
|
|
171647
|
+
return null;
|
|
171648
|
+
}
|
|
171649
|
+
}
|
|
171650
|
+
function extractTimebackCourses(config2) {
|
|
171651
|
+
const integrations = config2.integrations;
|
|
171652
|
+
if (!integrations)
|
|
171653
|
+
return;
|
|
171654
|
+
const timeback3 = integrations.timeback;
|
|
171655
|
+
if (!timeback3)
|
|
171656
|
+
return;
|
|
171657
|
+
const courses = timeback3.courses;
|
|
171658
|
+
if (!Array.isArray(courses) || courses.length === 0)
|
|
171659
|
+
return;
|
|
171660
|
+
return courses.map((course) => ({
|
|
171661
|
+
subject: course.subject,
|
|
171662
|
+
grade: course.grade,
|
|
171663
|
+
totalXp: course.totalXp,
|
|
171664
|
+
masterableUnits: course.masterableUnits
|
|
171665
|
+
}));
|
|
171666
|
+
}
|
|
171667
|
+
async function parseProjectInfo(options) {
|
|
171454
171668
|
if (!options.projectName || !options.projectSlug) {
|
|
171455
171669
|
return;
|
|
171456
171670
|
}
|
|
171671
|
+
const config2 = await loadPlaycademyConfig(options.configPath);
|
|
171672
|
+
const timebackCourses = config2 ? extractTimebackCourses(config2) : undefined;
|
|
171673
|
+
if (timebackCourses && timebackCourses.length > 0) {
|
|
171674
|
+
console.log(`[Sandbox] Found ${timebackCourses.length} TimeBack course(s) in config`);
|
|
171675
|
+
}
|
|
171457
171676
|
return {
|
|
171458
171677
|
slug: options.projectSlug,
|
|
171459
171678
|
displayName: options.projectName,
|
|
171460
|
-
version: "1.0.0"
|
|
171679
|
+
version: "1.0.0",
|
|
171680
|
+
timebackCourses
|
|
171461
171681
|
};
|
|
171462
171682
|
}
|
|
171463
171683
|
function parseTimebackOptions(options) {
|
|
@@ -171484,12 +171704,12 @@ function parseTimebackOptions(options) {
|
|
|
171484
171704
|
|
|
171485
171705
|
// src/cli/index.ts
|
|
171486
171706
|
var program2 = new Command;
|
|
171487
|
-
program2.name("playcademy-sandbox").description("Local development server for Playcademy game development").version(version3).option("-p, --port <number>", "Port to run the server on", "4321").option("-v, --verbose", "Enable verbose logging", false).option("--project-name <name>", "Name of the current project").option("--project-slug <slug>", "Slug of the current project").option("--realtime", "Enable the realtime server", false).option("--realtime-port <number>", "Port for the realtime server (defaults to main port + 1)").option("--no-seed", "Do not seed the database with demo data").option("--recreate-db", "Recreate the on-disk database on start", false).option("--memory", "Use in-memory database (no persistence)", false).option("--db-path <path>", "Custom path for the database file (relative to cwd or absolute)").option("--timeback-local", "Use local TimeBack instance").option("--timeback-oneroster-url <url>", "TimeBack OneRoster API URL").option("--timeback-caliper-url <url>", "TimeBack Caliper API URL").option("--timeback-course-id <id>", "TimeBack course ID for seeding").option("--timeback-student-id <id>", "TimeBack student ID for demo user").action(async (options) => {
|
|
171707
|
+
program2.name("playcademy-sandbox").description("Local development server for Playcademy game development").version(version3).option("-p, --port <number>", "Port to run the server on", "4321").option("-v, --verbose", "Enable verbose logging", false).option("--project-name <name>", "Name of the current project").option("--project-slug <slug>", "Slug of the current project").option("--realtime", "Enable the realtime server", false).option("--realtime-port <number>", "Port for the realtime server (defaults to main port + 1)").option("--no-seed", "Do not seed the database with demo data").option("--recreate-db", "Recreate the on-disk database on start", false).option("--memory", "Use in-memory database (no persistence)", false).option("--db-path <path>", "Custom path for the database file (relative to cwd or absolute)").option("--config-path <path>", "Path to playcademy.config.json (defaults to cwd)").option("--timeback-local", "Use local TimeBack instance").option("--timeback-oneroster-url <url>", "TimeBack OneRoster API URL").option("--timeback-caliper-url <url>", "TimeBack Caliper API URL").option("--timeback-course-id <id>", "TimeBack course ID for seeding").option("--timeback-student-id <id>", "TimeBack student ID for demo user").action(async (options) => {
|
|
171488
171708
|
try {
|
|
171489
171709
|
const requestedPort = parseInt(options.port);
|
|
171490
171710
|
const availablePort = await findAvailablePort(requestedPort);
|
|
171491
171711
|
const realtimePort = options.realtimePort ? parseInt(options.realtimePort) : availablePort + 1;
|
|
171492
|
-
const project = parseProjectInfo(options);
|
|
171712
|
+
const project = await parseProjectInfo(options);
|
|
171493
171713
|
const timebackOptions = parseTimebackOptions(options);
|
|
171494
171714
|
const servers = await startServer(availablePort, project, {
|
|
171495
171715
|
seed: options.seed,
|
package/dist/server.d.ts
CHANGED
|
@@ -20,6 +20,15 @@ interface TimebackConfig {
|
|
|
20
20
|
/**
|
|
21
21
|
* Project information types
|
|
22
22
|
*/
|
|
23
|
+
/**
|
|
24
|
+
* TimeBack course configuration from playcademy.config.json
|
|
25
|
+
*/
|
|
26
|
+
interface TimebackCourseConfig {
|
|
27
|
+
subject: string;
|
|
28
|
+
grade: number;
|
|
29
|
+
totalXp?: number | null;
|
|
30
|
+
masterableUnits?: number | null;
|
|
31
|
+
}
|
|
23
32
|
/**
|
|
24
33
|
* Information about the current game project
|
|
25
34
|
* Used for seeding and display
|
|
@@ -29,6 +38,8 @@ interface ProjectInfo {
|
|
|
29
38
|
displayName: string;
|
|
30
39
|
version: string;
|
|
31
40
|
description?: string;
|
|
41
|
+
/** TimeBack courses from playcademy.config.json integrations.timeback.courses */
|
|
42
|
+
timebackCourses?: TimebackCourseConfig[];
|
|
32
43
|
}
|
|
33
44
|
|
|
34
45
|
/**
|
package/dist/server.js
CHANGED
|
@@ -133793,7 +133793,7 @@ var serve = (options, listeningListener) => {
|
|
|
133793
133793
|
// package.json
|
|
133794
133794
|
var package_default = {
|
|
133795
133795
|
name: "@playcademy/sandbox",
|
|
133796
|
-
version: "0.2.
|
|
133796
|
+
version: "0.2.2",
|
|
133797
133797
|
description: "Local development server for Playcademy game development",
|
|
133798
133798
|
type: "module",
|
|
133799
133799
|
exports: {
|
|
@@ -157839,6 +157839,47 @@ var logger3 = {
|
|
|
157839
157839
|
warn: (msg) => getLogger().warn(msg),
|
|
157840
157840
|
error: (msg) => getLogger().error(msg)
|
|
157841
157841
|
};
|
|
157842
|
+
// src/database/seed/timeback.ts
|
|
157843
|
+
function resolveStudentId(studentId) {
|
|
157844
|
+
if (!studentId)
|
|
157845
|
+
return null;
|
|
157846
|
+
if (studentId === "mock")
|
|
157847
|
+
return `mock-student-${crypto.randomUUID().slice(0, 8)}`;
|
|
157848
|
+
return studentId;
|
|
157849
|
+
}
|
|
157850
|
+
function getAdminTimebackId() {
|
|
157851
|
+
return resolveStudentId(config.timeback.studentId);
|
|
157852
|
+
}
|
|
157853
|
+
async function seedTimebackIntegrations(db, gameId, courses) {
|
|
157854
|
+
const now2 = new Date;
|
|
157855
|
+
let seededCount = 0;
|
|
157856
|
+
for (const course of courses) {
|
|
157857
|
+
const courseId = `mock-${course.subject.toLowerCase()}-g${course.grade}`;
|
|
157858
|
+
try {
|
|
157859
|
+
const existing = await db.query.gameTimebackIntegrations.findFirst({
|
|
157860
|
+
where: (table14, { and: and3, eq: eq3 }) => and3(eq3(table14.gameId, gameId), eq3(table14.grade, course.grade), eq3(table14.subject, course.subject))
|
|
157861
|
+
});
|
|
157862
|
+
if (existing) {
|
|
157863
|
+
continue;
|
|
157864
|
+
}
|
|
157865
|
+
await db.insert(gameTimebackIntegrations).values({
|
|
157866
|
+
gameId,
|
|
157867
|
+
courseId,
|
|
157868
|
+
grade: course.grade,
|
|
157869
|
+
subject: course.subject,
|
|
157870
|
+
totalXp: course.totalXp ?? 1000,
|
|
157871
|
+
lastVerifiedAt: now2
|
|
157872
|
+
});
|
|
157873
|
+
seededCount++;
|
|
157874
|
+
} catch (error2) {
|
|
157875
|
+
console.error(`❌ Error seeding TimeBack integration for ${course.subject}:${course.grade}:`, error2);
|
|
157876
|
+
}
|
|
157877
|
+
}
|
|
157878
|
+
if (seededCount > 0) {
|
|
157879
|
+
logger3.info(`\uD83D\uDCDA Seeded ${seededCount} TimeBack integration(s)`);
|
|
157880
|
+
}
|
|
157881
|
+
}
|
|
157882
|
+
|
|
157842
157883
|
// src/database/seed/games.ts
|
|
157843
157884
|
async function seedCoreGames(db) {
|
|
157844
157885
|
const now2 = new Date;
|
|
@@ -157873,6 +157914,9 @@ async function seedCurrentProjectGame(db, project) {
|
|
|
157873
157914
|
});
|
|
157874
157915
|
if (existingGame) {
|
|
157875
157916
|
logger3.info(`\uD83C\uDFAE Game "${project.displayName}" (${project.slug}) already exists`);
|
|
157917
|
+
if (project.timebackCourses && project.timebackCourses.length > 0) {
|
|
157918
|
+
await seedTimebackIntegrations(db, existingGame.id, project.timebackCourses);
|
|
157919
|
+
}
|
|
157876
157920
|
return existingGame;
|
|
157877
157921
|
}
|
|
157878
157922
|
const gameRecord = {
|
|
@@ -157892,6 +157936,12 @@ async function seedCurrentProjectGame(db, project) {
|
|
|
157892
157936
|
updatedAt: now2
|
|
157893
157937
|
};
|
|
157894
157938
|
const [newGame] = await db.insert(games).values(gameRecord).returning();
|
|
157939
|
+
if (!newGame) {
|
|
157940
|
+
throw new Error("Failed to create game record");
|
|
157941
|
+
}
|
|
157942
|
+
if (project.timebackCourses && project.timebackCourses.length > 0) {
|
|
157943
|
+
await seedTimebackIntegrations(db, newGame.id, project.timebackCourses);
|
|
157944
|
+
}
|
|
157895
157945
|
return newGame;
|
|
157896
157946
|
} catch (error2) {
|
|
157897
157947
|
console.error("❌ Error seeding project game:", error2);
|
|
@@ -157953,18 +158003,6 @@ async function seedSpriteTemplates(db) {
|
|
|
157953
158003
|
}
|
|
157954
158004
|
}
|
|
157955
158005
|
|
|
157956
|
-
// src/database/seed/timeback.ts
|
|
157957
|
-
function resolveStudentId(studentId) {
|
|
157958
|
-
if (!studentId)
|
|
157959
|
-
return null;
|
|
157960
|
-
if (studentId === "mock")
|
|
157961
|
-
return `mock-student-${crypto.randomUUID().slice(0, 8)}`;
|
|
157962
|
-
return studentId;
|
|
157963
|
-
}
|
|
157964
|
-
function getAdminTimebackId() {
|
|
157965
|
-
return resolveStudentId(config.timeback.studentId);
|
|
157966
|
-
}
|
|
157967
|
-
|
|
157968
158006
|
// src/database/seed/index.ts
|
|
157969
158007
|
async function seedDemoData(db) {
|
|
157970
158008
|
try {
|