@playcademy/vite-plugin 0.1.38 → 0.2.0
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/README.md +76 -17
- package/dist/index.js +1459 -1010
- package/dist/lib/logging/adapter.d.ts +2 -2
- package/dist/lib/logging/index.d.ts +2 -2
- package/dist/lib/logging/utils.d.ts +11 -10
- package/dist/lib/sandbox/index.d.ts +1 -1
- package/dist/lib/sandbox/project-info.d.ts +5 -2
- package/dist/lib/sandbox/server.d.ts +3 -3
- package/dist/lib/sandbox/timeback.d.ts +31 -6
- package/dist/lib/sandbox/token.d.ts +25 -0
- package/dist/server/config-watcher.d.ts +15 -0
- package/dist/server/hotkeys/cycle-platform-role.d.ts +9 -0
- package/dist/server/middleware.d.ts +3 -4
- package/dist/server/recreate-sandbox.d.ts +21 -0
- package/dist/server/state.d.ts +10 -1
- package/dist/types/index.d.ts +3 -3
- package/dist/types/internal.d.ts +70 -8
- package/dist/types/options.d.ts +61 -32
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -40990,105 +40990,6 @@ function closeBundleHook(context) {
|
|
|
40990
40990
|
// src/hooks/config.ts
|
|
40991
40991
|
import { DEFAULT_PORTS } from "playcademy/constants";
|
|
40992
40992
|
|
|
40993
|
-
// ../utils/src/port.ts
|
|
40994
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
40995
|
-
import { createServer } from "node:net";
|
|
40996
|
-
import { homedir } from "node:os";
|
|
40997
|
-
import { join } from "node:path";
|
|
40998
|
-
async function isPortAvailableOnHost(port, host) {
|
|
40999
|
-
return new Promise((resolve) => {
|
|
41000
|
-
const server = createServer();
|
|
41001
|
-
let resolved = false;
|
|
41002
|
-
const cleanup = (result) => {
|
|
41003
|
-
if (resolved)
|
|
41004
|
-
return;
|
|
41005
|
-
resolved = true;
|
|
41006
|
-
try {
|
|
41007
|
-
server.close();
|
|
41008
|
-
} catch {}
|
|
41009
|
-
resolve(result);
|
|
41010
|
-
};
|
|
41011
|
-
const timeout = setTimeout(() => cleanup(true), 100);
|
|
41012
|
-
server.once("error", (err2) => {
|
|
41013
|
-
clearTimeout(timeout);
|
|
41014
|
-
if (err2.code === "EAFNOSUPPORT" || err2.code === "EADDRNOTAVAIL") {
|
|
41015
|
-
cleanup(true);
|
|
41016
|
-
} else {
|
|
41017
|
-
cleanup(false);
|
|
41018
|
-
}
|
|
41019
|
-
});
|
|
41020
|
-
server.once("listening", () => {
|
|
41021
|
-
clearTimeout(timeout);
|
|
41022
|
-
cleanup(true);
|
|
41023
|
-
});
|
|
41024
|
-
server.listen(port, host).unref();
|
|
41025
|
-
});
|
|
41026
|
-
}
|
|
41027
|
-
async function findAvailablePort(startPort = 4321) {
|
|
41028
|
-
if (await isPortAvailableOnHost(startPort, "0.0.0.0")) {
|
|
41029
|
-
return startPort;
|
|
41030
|
-
} else {
|
|
41031
|
-
return findAvailablePort(startPort + 1);
|
|
41032
|
-
}
|
|
41033
|
-
}
|
|
41034
|
-
function getRegistryPath() {
|
|
41035
|
-
const home = homedir();
|
|
41036
|
-
const dir = join(home, ".playcademy");
|
|
41037
|
-
if (!existsSync(dir)) {
|
|
41038
|
-
mkdirSync(dir, { recursive: true });
|
|
41039
|
-
}
|
|
41040
|
-
return join(dir, ".proc");
|
|
41041
|
-
}
|
|
41042
|
-
function readRegistry() {
|
|
41043
|
-
const registryPath = getRegistryPath();
|
|
41044
|
-
if (!existsSync(registryPath)) {
|
|
41045
|
-
return {};
|
|
41046
|
-
}
|
|
41047
|
-
try {
|
|
41048
|
-
const content = readFileSync(registryPath, "utf-8");
|
|
41049
|
-
return JSON.parse(content);
|
|
41050
|
-
} catch {
|
|
41051
|
-
return {};
|
|
41052
|
-
}
|
|
41053
|
-
}
|
|
41054
|
-
function writeRegistry(registry) {
|
|
41055
|
-
const registryPath = getRegistryPath();
|
|
41056
|
-
writeFileSync(registryPath, JSON.stringify(registry, null, 2), "utf-8");
|
|
41057
|
-
}
|
|
41058
|
-
function getServerKey(type, port) {
|
|
41059
|
-
return `${type}-${port}`;
|
|
41060
|
-
}
|
|
41061
|
-
function writeServerInfo(type, info2) {
|
|
41062
|
-
const registry = readRegistry();
|
|
41063
|
-
const key = getServerKey(type, info2.port);
|
|
41064
|
-
registry[key] = info2;
|
|
41065
|
-
writeRegistry(registry);
|
|
41066
|
-
}
|
|
41067
|
-
function cleanupServerInfo(type, projectRoot, pid) {
|
|
41068
|
-
const registry = readRegistry();
|
|
41069
|
-
const keysToRemove = [];
|
|
41070
|
-
for (const [key, info2] of Object.entries(registry)) {
|
|
41071
|
-
if (key.startsWith(`${type}-`)) {
|
|
41072
|
-
let matches = true;
|
|
41073
|
-
if (projectRoot && info2.projectRoot !== projectRoot) {
|
|
41074
|
-
matches = false;
|
|
41075
|
-
}
|
|
41076
|
-
if (pid !== undefined && info2.pid !== pid) {
|
|
41077
|
-
matches = false;
|
|
41078
|
-
}
|
|
41079
|
-
if (matches) {
|
|
41080
|
-
keysToRemove.push(key);
|
|
41081
|
-
}
|
|
41082
|
-
}
|
|
41083
|
-
}
|
|
41084
|
-
for (const key of keysToRemove) {
|
|
41085
|
-
delete registry[key];
|
|
41086
|
-
}
|
|
41087
|
-
if (keysToRemove.length > 0) {
|
|
41088
|
-
writeRegistry(registry);
|
|
41089
|
-
}
|
|
41090
|
-
}
|
|
41091
|
-
|
|
41092
40993
|
// src/config/proxy.ts
|
|
41093
40994
|
function createProxyConfig(existingProxy, backendPort) {
|
|
41094
40995
|
if (existingProxy["/api"]) {
|
|
@@ -41122,7 +41023,8 @@ function createViteConfig(userConfig, backendPort) {
|
|
|
41122
41023
|
// src/hooks/config.ts
|
|
41123
41024
|
async function configHook(userConfig, context) {
|
|
41124
41025
|
process.noDeprecation = true;
|
|
41125
|
-
context.backendPort =
|
|
41026
|
+
context.backendPort = DEFAULT_PORTS.BACKEND;
|
|
41027
|
+
context.sandboxPort = DEFAULT_PORTS.SANDBOX;
|
|
41126
41028
|
return createViteConfig(userConfig, context.backendPort);
|
|
41127
41029
|
}
|
|
41128
41030
|
|
|
@@ -41141,8 +41043,12 @@ var serverState = {
|
|
|
41141
41043
|
backend: null,
|
|
41142
41044
|
viteServer: null,
|
|
41143
41045
|
currentMode: "platform",
|
|
41144
|
-
timebackRoleOverride: null
|
|
41046
|
+
timebackRoleOverride: null,
|
|
41047
|
+
platformRoleOverride: null
|
|
41145
41048
|
};
|
|
41049
|
+
function getSandboxRef() {
|
|
41050
|
+
return serverState.sandbox;
|
|
41051
|
+
}
|
|
41146
41052
|
function hasActiveServers() {
|
|
41147
41053
|
return !!(serverState.backend || serverState.sandbox);
|
|
41148
41054
|
}
|
|
@@ -41164,6 +41070,12 @@ function getTimebackRoleOverride() {
|
|
|
41164
41070
|
function setTimebackRoleOverride(role) {
|
|
41165
41071
|
serverState.timebackRoleOverride = role;
|
|
41166
41072
|
}
|
|
41073
|
+
function getPlatformRoleOverride() {
|
|
41074
|
+
return serverState.platformRoleOverride;
|
|
41075
|
+
}
|
|
41076
|
+
function setPlatformRoleOverride(role) {
|
|
41077
|
+
serverState.platformRoleOverride = role;
|
|
41078
|
+
}
|
|
41167
41079
|
|
|
41168
41080
|
// src/server/cleanup.ts
|
|
41169
41081
|
async function cleanupServers() {
|
|
@@ -41172,23 +41084,29 @@ async function cleanupServers() {
|
|
|
41172
41084
|
await serverState.backend.server.dispose();
|
|
41173
41085
|
serverState.backend.stopHotReload();
|
|
41174
41086
|
serverState.backend.cleanup();
|
|
41087
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
41175
41088
|
} catch {}
|
|
41176
41089
|
serverState.backend = null;
|
|
41177
41090
|
}
|
|
41178
41091
|
if (serverState.sandbox) {
|
|
41179
41092
|
try {
|
|
41180
|
-
serverState.sandbox.cleanup();
|
|
41093
|
+
await serverState.sandbox.cleanup();
|
|
41181
41094
|
} catch {}
|
|
41182
41095
|
serverState.sandbox = null;
|
|
41183
41096
|
}
|
|
41184
41097
|
}
|
|
41185
41098
|
|
|
41186
|
-
// src/server/
|
|
41187
|
-
|
|
41099
|
+
// src/server/config-watcher.ts
|
|
41100
|
+
import fs8 from "node:fs";
|
|
41101
|
+
import path6 from "node:path";
|
|
41102
|
+
import { CONFIG_FILE_NAMES } from "playcademy/constants";
|
|
41188
41103
|
|
|
41189
|
-
// src/
|
|
41190
|
-
var
|
|
41191
|
-
|
|
41104
|
+
// src/server/recreate-sandbox.ts
|
|
41105
|
+
var import_picocolors6 = __toESM(require_picocolors(), 1);
|
|
41106
|
+
|
|
41107
|
+
// ../utils/src/vite-logger.ts
|
|
41108
|
+
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
41109
|
+
var { bold, cyan, dim } = import_picocolors2.default;
|
|
41192
41110
|
function formatTimestamp() {
|
|
41193
41111
|
const now = new Date;
|
|
41194
41112
|
const hours = now.getHours();
|
|
@@ -41196,32 +41114,19 @@ function formatTimestamp() {
|
|
|
41196
41114
|
const seconds = now.getSeconds().toString().padStart(2, "0");
|
|
41197
41115
|
const ampm = hours >= 12 ? "PM" : "AM";
|
|
41198
41116
|
const displayHours = hours % 12 || 12;
|
|
41199
|
-
return
|
|
41117
|
+
return dim(`${displayHours}:${minutes}:${seconds} ${ampm}`);
|
|
41200
41118
|
}
|
|
41201
|
-
function
|
|
41202
|
-
const
|
|
41203
|
-
const
|
|
41204
|
-
|
|
41205
|
-
|
|
41206
|
-
setTimebackRoleOverride(nextRole);
|
|
41207
|
-
logger.info(`${formatTimestamp()} ${import_picocolors2.default.cyan(import_picocolors2.default.bold("[playcademy]"))} ${import_picocolors2.default.dim("(timeback)")} ${import_picocolors2.default.red(currentRole)} → ${import_picocolors2.default.green(nextRole)}`);
|
|
41208
|
-
if (getViteServerRef()) {
|
|
41209
|
-
getViteServerRef()?.ws.send({ type: "full-reload", path: "*" });
|
|
41210
|
-
} else {
|
|
41211
|
-
logger.warn(`${formatTimestamp()} ${import_picocolors2.default.red(import_picocolors2.bold("[playcademy]"))} ${import_picocolors2.dim("(timeback)")} ${import_picocolors2.yellow("Cannot cycle TimeBack role: no Vite server reference")}`);
|
|
41119
|
+
function createLogPrefix(entity, domain) {
|
|
41120
|
+
const timestamp = formatTimestamp();
|
|
41121
|
+
const label = bold(cyan(`[${entity}]`));
|
|
41122
|
+
if (domain) {
|
|
41123
|
+
return `${timestamp} ${label} ${dim(`(${domain})`)}`;
|
|
41212
41124
|
}
|
|
41125
|
+
return `${timestamp} ${label}`;
|
|
41213
41126
|
}
|
|
41214
|
-
var cycleTimebackRoleHotkey = (options) => ({
|
|
41215
|
-
key: "t",
|
|
41216
|
-
description: "cycle TimeBack role",
|
|
41217
|
-
action: () => cycleTimebackRole(options.viteConfig.logger)
|
|
41218
|
-
});
|
|
41219
|
-
|
|
41220
|
-
// src/server/hotkeys/recreate-database.ts
|
|
41221
|
-
var import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
41222
41127
|
|
|
41223
41128
|
// src/lib/sandbox/server.ts
|
|
41224
|
-
var
|
|
41129
|
+
var import_picocolors5 = __toESM(require_picocolors(), 1);
|
|
41225
41130
|
import { DEFAULT_PORTS as DEFAULT_PORTS2 } from "playcademy/constants";
|
|
41226
41131
|
|
|
41227
41132
|
// ../sandbox/dist/server.js
|
|
@@ -41258,20 +41163,24 @@ import { Http2ServerRequest as Http2ServerRequest2 } from "http2";
|
|
|
41258
41163
|
import { Http2ServerRequest } from "http2";
|
|
41259
41164
|
import { Readable } from "stream";
|
|
41260
41165
|
import crypto2 from "crypto";
|
|
41166
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "node:fs";
|
|
41167
|
+
import { createServer } from "node:net";
|
|
41168
|
+
import { homedir } from "node:os";
|
|
41169
|
+
import { join as join2 } from "node:path";
|
|
41261
41170
|
import fs5 from "node:fs";
|
|
41262
41171
|
import fs3 from "node:fs";
|
|
41263
41172
|
import fs22 from "node:fs";
|
|
41264
|
-
import { dirname, isAbsolute, join as
|
|
41173
|
+
import { dirname, isAbsolute, join as join3 } from "node:path";
|
|
41265
41174
|
import path2 from "path";
|
|
41266
41175
|
import { stdout } from "process";
|
|
41267
41176
|
import { createPublicKey as createPublicKey2, createVerify, verify as verify3 } from "crypto";
|
|
41268
41177
|
import { request as request2 } from "https";
|
|
41269
41178
|
import { pipeline } from "stream";
|
|
41270
41179
|
import { readdir as readdir2, readFile as readFile2, stat } from "node:fs/promises";
|
|
41271
|
-
import { join as
|
|
41180
|
+
import { join as join5, relative } from "node:path";
|
|
41272
41181
|
import { createHash } from "node:crypto";
|
|
41273
41182
|
import { readdir, readFile } from "node:fs/promises";
|
|
41274
|
-
import { join as
|
|
41183
|
+
import { join as join4 } from "node:path";
|
|
41275
41184
|
import crypto6 from "node:crypto";
|
|
41276
41185
|
import * as crypto7 from "node:crypto";
|
|
41277
41186
|
var __create2 = Object.create;
|
|
@@ -45601,7 +45510,7 @@ var require_node22 = __commonJS2((exports) => {
|
|
|
45601
45510
|
return path3;
|
|
45602
45511
|
}
|
|
45603
45512
|
exports2.normalize = normalize;
|
|
45604
|
-
function
|
|
45513
|
+
function join22(aRoot, aPath) {
|
|
45605
45514
|
if (aRoot === "") {
|
|
45606
45515
|
aRoot = ".";
|
|
45607
45516
|
}
|
|
@@ -45633,7 +45542,7 @@ var require_node22 = __commonJS2((exports) => {
|
|
|
45633
45542
|
}
|
|
45634
45543
|
return joined;
|
|
45635
45544
|
}
|
|
45636
|
-
exports2.join =
|
|
45545
|
+
exports2.join = join22;
|
|
45637
45546
|
exports2.isAbsolute = function(aPath) {
|
|
45638
45547
|
return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
|
|
45639
45548
|
};
|
|
@@ -45806,7 +45715,7 @@ var require_node22 = __commonJS2((exports) => {
|
|
|
45806
45715
|
parsed.path = parsed.path.substring(0, index2 + 1);
|
|
45807
45716
|
}
|
|
45808
45717
|
}
|
|
45809
|
-
sourceURL =
|
|
45718
|
+
sourceURL = join22(urlGenerate(parsed), sourceURL);
|
|
45810
45719
|
}
|
|
45811
45720
|
return normalize(sourceURL);
|
|
45812
45721
|
}
|
|
@@ -49532,16 +49441,16 @@ If you have no idea what this means or what Pirates is, let me explain: Pirates
|
|
|
49532
49441
|
return walkForTsConfig(parentDirectory, readdirSync);
|
|
49533
49442
|
}
|
|
49534
49443
|
exports2.walkForTsConfig = walkForTsConfig;
|
|
49535
|
-
function loadTsconfig(configFilePath,
|
|
49536
|
-
if (
|
|
49537
|
-
|
|
49444
|
+
function loadTsconfig(configFilePath, existsSync3, readFileSync2) {
|
|
49445
|
+
if (existsSync3 === undefined) {
|
|
49446
|
+
existsSync3 = fs32.existsSync;
|
|
49538
49447
|
}
|
|
49539
49448
|
if (readFileSync2 === undefined) {
|
|
49540
49449
|
readFileSync2 = function(filename) {
|
|
49541
49450
|
return fs32.readFileSync(filename, "utf8");
|
|
49542
49451
|
};
|
|
49543
49452
|
}
|
|
49544
|
-
if (!
|
|
49453
|
+
if (!existsSync3(configFilePath)) {
|
|
49545
49454
|
return;
|
|
49546
49455
|
}
|
|
49547
49456
|
var configString = readFileSync2(configFilePath);
|
|
@@ -49557,27 +49466,27 @@ If you have no idea what this means or what Pirates is, let me explain: Pirates
|
|
|
49557
49466
|
var base = undefined;
|
|
49558
49467
|
if (Array.isArray(extendedConfig)) {
|
|
49559
49468
|
base = extendedConfig.reduce(function(currBase, extendedConfigElement) {
|
|
49560
|
-
return mergeTsconfigs(currBase, loadTsconfigFromExtends(configFilePath, extendedConfigElement,
|
|
49469
|
+
return mergeTsconfigs(currBase, loadTsconfigFromExtends(configFilePath, extendedConfigElement, existsSync3, readFileSync2));
|
|
49561
49470
|
}, {});
|
|
49562
49471
|
} else {
|
|
49563
|
-
base = loadTsconfigFromExtends(configFilePath, extendedConfig,
|
|
49472
|
+
base = loadTsconfigFromExtends(configFilePath, extendedConfig, existsSync3, readFileSync2);
|
|
49564
49473
|
}
|
|
49565
49474
|
return mergeTsconfigs(base, config2);
|
|
49566
49475
|
}
|
|
49567
49476
|
return config2;
|
|
49568
49477
|
}
|
|
49569
49478
|
exports2.loadTsconfig = loadTsconfig;
|
|
49570
|
-
function loadTsconfigFromExtends(configFilePath, extendedConfigValue,
|
|
49479
|
+
function loadTsconfigFromExtends(configFilePath, extendedConfigValue, existsSync3, readFileSync2) {
|
|
49571
49480
|
var _a;
|
|
49572
49481
|
if (typeof extendedConfigValue === "string" && extendedConfigValue.indexOf(".json") === -1) {
|
|
49573
49482
|
extendedConfigValue += ".json";
|
|
49574
49483
|
}
|
|
49575
49484
|
var currentDir = path3.dirname(configFilePath);
|
|
49576
49485
|
var extendedConfigPath = path3.join(currentDir, extendedConfigValue);
|
|
49577
|
-
if (extendedConfigValue.indexOf("/") !== -1 && extendedConfigValue.indexOf(".") !== -1 && !
|
|
49486
|
+
if (extendedConfigValue.indexOf("/") !== -1 && extendedConfigValue.indexOf(".") !== -1 && !existsSync3(extendedConfigPath)) {
|
|
49578
49487
|
extendedConfigPath = path3.join(currentDir, "node_modules", extendedConfigValue);
|
|
49579
49488
|
}
|
|
49580
|
-
var config2 = loadTsconfig(extendedConfigPath,
|
|
49489
|
+
var config2 = loadTsconfig(extendedConfigPath, existsSync3, readFileSync2) || {};
|
|
49581
49490
|
if ((_a = config2.compilerOptions) === null || _a === undefined ? undefined : _a.baseUrl) {
|
|
49582
49491
|
var extendsDir = path3.dirname(extendedConfigValue);
|
|
49583
49492
|
config2.compilerOptions.baseUrl = path3.join(extendsDir, config2.compilerOptions.baseUrl);
|
|
@@ -76048,7 +75957,7 @@ globstar while`, file, fr, pattern, pr2, swallowee);
|
|
|
76048
75957
|
return new SQL2([new StringChunk2(str)]);
|
|
76049
75958
|
}
|
|
76050
75959
|
sql22.raw = raw2;
|
|
76051
|
-
function
|
|
75960
|
+
function join42(chunks, separator) {
|
|
76052
75961
|
const result = [];
|
|
76053
75962
|
for (const [i3, chunk] of chunks.entries()) {
|
|
76054
75963
|
if (i3 > 0 && separator !== undefined) {
|
|
@@ -76058,7 +75967,7 @@ globstar while`, file, fr, pattern, pr2, swallowee);
|
|
|
76058
75967
|
}
|
|
76059
75968
|
return new SQL2(result);
|
|
76060
75969
|
}
|
|
76061
|
-
sql22.join =
|
|
75970
|
+
sql22.join = join42;
|
|
76062
75971
|
function identifier(value) {
|
|
76063
75972
|
return new Name2(value);
|
|
76064
75973
|
}
|
|
@@ -78888,7 +78797,7 @@ params: ${params}`);
|
|
|
78888
78797
|
const tableName = getTableLikeName2(table62);
|
|
78889
78798
|
for (const item of extractUsedTable(table62))
|
|
78890
78799
|
this.usedTables.add(item);
|
|
78891
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
78800
|
+
if (typeof tableName === "string" && this.config.joins?.some((join42) => join42.alias === tableName)) {
|
|
78892
78801
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
78893
78802
|
}
|
|
78894
78803
|
if (!this.isPartialSelect) {
|
|
@@ -79746,7 +79655,7 @@ params: ${params}`);
|
|
|
79746
79655
|
createJoin(joinType) {
|
|
79747
79656
|
return (table62, on2) => {
|
|
79748
79657
|
const tableName = getTableLikeName2(table62);
|
|
79749
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
79658
|
+
if (typeof tableName === "string" && this.config.joins.some((join42) => join42.alias === tableName)) {
|
|
79750
79659
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
79751
79660
|
}
|
|
79752
79661
|
if (typeof on2 === "function") {
|
|
@@ -79792,10 +79701,10 @@ params: ${params}`);
|
|
|
79792
79701
|
const fromFields = this.getTableLikeFields(this.config.from);
|
|
79793
79702
|
fields[tableName] = fromFields;
|
|
79794
79703
|
}
|
|
79795
|
-
for (const
|
|
79796
|
-
const tableName2 = getTableLikeName2(
|
|
79797
|
-
if (typeof tableName2 === "string" && !is2(
|
|
79798
|
-
const fromFields = this.getTableLikeFields(
|
|
79704
|
+
for (const join42 of this.config.joins) {
|
|
79705
|
+
const tableName2 = getTableLikeName2(join42.table);
|
|
79706
|
+
if (typeof tableName2 === "string" && !is2(join42.table, SQL2)) {
|
|
79707
|
+
const fromFields = this.getTableLikeFields(join42.table);
|
|
79799
79708
|
fields[tableName2] = fromFields;
|
|
79800
79709
|
}
|
|
79801
79710
|
}
|
|
@@ -83370,7 +83279,7 @@ ORDER BY
|
|
|
83370
83279
|
const tableName = getTableLikeName2(table62);
|
|
83371
83280
|
for (const item of extractUsedTable2(table62))
|
|
83372
83281
|
this.usedTables.add(item);
|
|
83373
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
83282
|
+
if (typeof tableName === "string" && this.config.joins?.some((join42) => join42.alias === tableName)) {
|
|
83374
83283
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
83375
83284
|
}
|
|
83376
83285
|
if (!this.isPartialSelect) {
|
|
@@ -83811,7 +83720,7 @@ ORDER BY
|
|
|
83811
83720
|
createJoin(joinType) {
|
|
83812
83721
|
return (table62, on2) => {
|
|
83813
83722
|
const tableName = getTableLikeName2(table62);
|
|
83814
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
83723
|
+
if (typeof tableName === "string" && this.config.joins.some((join42) => join42.alias === tableName)) {
|
|
83815
83724
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
83816
83725
|
}
|
|
83817
83726
|
if (typeof on2 === "function") {
|
|
@@ -87418,7 +87327,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
87418
87327
|
const tableName = getTableLikeName2(table62);
|
|
87419
87328
|
for (const item of extractUsedTable3(table62))
|
|
87420
87329
|
this.usedTables.add(item);
|
|
87421
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
87330
|
+
if (typeof tableName === "string" && this.config.joins?.some((join42) => join42.alias === tableName)) {
|
|
87422
87331
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
87423
87332
|
}
|
|
87424
87333
|
if (!this.isPartialSelect) {
|
|
@@ -91460,7 +91369,7 @@ AND
|
|
|
91460
91369
|
const tableName = getTableLikeName2(table62);
|
|
91461
91370
|
for (const item of extractUsedTable4(table62))
|
|
91462
91371
|
this.usedTables.add(item);
|
|
91463
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
91372
|
+
if (typeof tableName === "string" && this.config.joins?.some((join42) => join42.alias === tableName)) {
|
|
91464
91373
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
91465
91374
|
}
|
|
91466
91375
|
if (!this.isPartialSelect) {
|
|
@@ -162051,10 +161960,10 @@ var require_colorette = __commonJS2((exports) => {
|
|
|
162051
161960
|
black,
|
|
162052
161961
|
red,
|
|
162053
161962
|
green,
|
|
162054
|
-
yellow
|
|
161963
|
+
yellow,
|
|
162055
161964
|
blue,
|
|
162056
161965
|
magenta,
|
|
162057
|
-
cyan,
|
|
161966
|
+
cyan: cyan2,
|
|
162058
161967
|
white,
|
|
162059
161968
|
gray,
|
|
162060
161969
|
bgBlack,
|
|
@@ -162104,7 +162013,7 @@ var require_colorette = __commonJS2((exports) => {
|
|
|
162104
162013
|
exports.blueBright = blueBright;
|
|
162105
162014
|
exports.bold = bold2;
|
|
162106
162015
|
exports.createColors = createColors;
|
|
162107
|
-
exports.cyan =
|
|
162016
|
+
exports.cyan = cyan2;
|
|
162108
162017
|
exports.cyanBright = cyanBright;
|
|
162109
162018
|
exports.dim = dim2;
|
|
162110
162019
|
exports.gray = gray;
|
|
@@ -162123,7 +162032,7 @@ var require_colorette = __commonJS2((exports) => {
|
|
|
162123
162032
|
exports.underline = underline;
|
|
162124
162033
|
exports.white = white;
|
|
162125
162034
|
exports.whiteBright = whiteBright;
|
|
162126
|
-
exports.yellow =
|
|
162035
|
+
exports.yellow = yellow;
|
|
162127
162036
|
exports.yellowBright = yellowBright;
|
|
162128
162037
|
});
|
|
162129
162038
|
var require_dist22 = __commonJS2((exports) => {
|
|
@@ -175066,7 +174975,7 @@ function hasTimebackCredentials() {
|
|
|
175066
174975
|
return false;
|
|
175067
174976
|
}
|
|
175068
174977
|
function hasTimebackFullConfig() {
|
|
175069
|
-
return hasTimebackCredentials() && !!(config.timeback.courseId && config.timeback.
|
|
174978
|
+
return hasTimebackCredentials() && !!(config.timeback.courseId && config.timeback.timebackId);
|
|
175070
174979
|
}
|
|
175071
174980
|
function requireTimebackCredentials() {
|
|
175072
174981
|
if (hasTimebackCredentials())
|
|
@@ -175120,11 +175029,36 @@ function configureTimeback(options) {
|
|
|
175120
175029
|
config.timeback.courseId = options.courseId;
|
|
175121
175030
|
process.env.SANDBOX_TIMEBACK_COURSE_ID = options.courseId;
|
|
175122
175031
|
}
|
|
175123
|
-
if (options.
|
|
175124
|
-
config.timeback.
|
|
175125
|
-
process.env.SANDBOX_TIMEBACK_STUDENT_ID = options.
|
|
175032
|
+
if (options.timebackId) {
|
|
175033
|
+
config.timeback.timebackId = options.timebackId;
|
|
175034
|
+
process.env.SANDBOX_TIMEBACK_STUDENT_ID = options.timebackId;
|
|
175035
|
+
const isMockMode = options.timebackId === "mock";
|
|
175036
|
+
process.env.SANDBOX_TIMEBACK_MOCK_MODE = isMockMode ? "true" : "false";
|
|
175037
|
+
}
|
|
175038
|
+
if (options.organization) {
|
|
175039
|
+
config.timeback.organization = options.organization;
|
|
175040
|
+
process.env.SANDBOX_TIMEBACK_ORG_ID = options.organization.id;
|
|
175041
|
+
if (options.organization.name) {
|
|
175042
|
+
process.env.SANDBOX_TIMEBACK_ORG_NAME = options.organization.name;
|
|
175043
|
+
}
|
|
175044
|
+
if (options.organization.type) {
|
|
175045
|
+
process.env.SANDBOX_TIMEBACK_ORG_TYPE = options.organization.type;
|
|
175046
|
+
}
|
|
175047
|
+
}
|
|
175048
|
+
if (options.role) {
|
|
175049
|
+
config.timeback.role = options.role;
|
|
175050
|
+
process.env.SANDBOX_TIMEBACK_ROLE = options.role;
|
|
175126
175051
|
}
|
|
175127
175052
|
}
|
|
175053
|
+
function getTimebackDisplayMode() {
|
|
175054
|
+
const { timebackId, mode } = config.timeback;
|
|
175055
|
+
if (!timebackId)
|
|
175056
|
+
return null;
|
|
175057
|
+
if (timebackId === "mock" || !hasTimebackCredentials()) {
|
|
175058
|
+
return "mock";
|
|
175059
|
+
}
|
|
175060
|
+
return mode;
|
|
175061
|
+
}
|
|
175128
175062
|
function setEmbeddedMode(embedded) {
|
|
175129
175063
|
config.embedded = embedded;
|
|
175130
175064
|
process.env.PLAYCADEMY_EMBEDDED = embedded ? "1" : undefined;
|
|
@@ -175143,12 +175077,199 @@ var config = {
|
|
|
175143
175077
|
clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET,
|
|
175144
175078
|
authUrl: process.env.TIMEBACK_API_AUTH_URL,
|
|
175145
175079
|
courseId: process.env.SANDBOX_TIMEBACK_COURSE_ID,
|
|
175146
|
-
|
|
175080
|
+
timebackId: process.env.SANDBOX_TIMEBACK_STUDENT_ID
|
|
175147
175081
|
}
|
|
175148
175082
|
};
|
|
175149
175083
|
process.env.BETTER_AUTH_SECRET = config.auth.betterAuthSecret;
|
|
175150
175084
|
process.env.GAME_JWT_SECRET = config.auth.gameJwtSecret;
|
|
175151
175085
|
process.env.PUBLIC_IS_LOCAL = "true";
|
|
175086
|
+
var now = new Date;
|
|
175087
|
+
var DEMO_USER_IDS = {
|
|
175088
|
+
player: "00000000-0000-0000-0000-000000000001",
|
|
175089
|
+
developer: "00000000-0000-0000-0000-000000000002",
|
|
175090
|
+
admin: "00000000-0000-0000-0000-000000000003",
|
|
175091
|
+
pendingDeveloper: "00000000-0000-0000-0000-000000000004",
|
|
175092
|
+
unverifiedPlayer: "00000000-0000-0000-0000-000000000005"
|
|
175093
|
+
};
|
|
175094
|
+
var DEMO_USERS = {
|
|
175095
|
+
admin: {
|
|
175096
|
+
id: DEMO_USER_IDS.admin,
|
|
175097
|
+
name: "Admin User",
|
|
175098
|
+
username: "admin_user",
|
|
175099
|
+
email: "admin@playcademy.com",
|
|
175100
|
+
emailVerified: true,
|
|
175101
|
+
image: null,
|
|
175102
|
+
role: "admin",
|
|
175103
|
+
developerStatus: "approved",
|
|
175104
|
+
createdAt: now,
|
|
175105
|
+
updatedAt: now
|
|
175106
|
+
},
|
|
175107
|
+
player: {
|
|
175108
|
+
id: DEMO_USER_IDS.player,
|
|
175109
|
+
name: "Player User",
|
|
175110
|
+
username: "player_user",
|
|
175111
|
+
email: "player@playcademy.com",
|
|
175112
|
+
emailVerified: true,
|
|
175113
|
+
image: null,
|
|
175114
|
+
role: "player",
|
|
175115
|
+
developerStatus: "none",
|
|
175116
|
+
createdAt: now,
|
|
175117
|
+
updatedAt: now
|
|
175118
|
+
},
|
|
175119
|
+
developer: {
|
|
175120
|
+
id: DEMO_USER_IDS.developer,
|
|
175121
|
+
name: "Developer User",
|
|
175122
|
+
username: "developer_user",
|
|
175123
|
+
email: "developer@playcademy.com",
|
|
175124
|
+
emailVerified: true,
|
|
175125
|
+
image: null,
|
|
175126
|
+
role: "developer",
|
|
175127
|
+
developerStatus: "approved",
|
|
175128
|
+
createdAt: now,
|
|
175129
|
+
updatedAt: now
|
|
175130
|
+
},
|
|
175131
|
+
pendingDeveloper: {
|
|
175132
|
+
id: DEMO_USER_IDS.pendingDeveloper,
|
|
175133
|
+
name: "Pending Developer",
|
|
175134
|
+
username: "pending_dev",
|
|
175135
|
+
email: "pending@playcademy.com",
|
|
175136
|
+
emailVerified: true,
|
|
175137
|
+
image: null,
|
|
175138
|
+
role: "developer",
|
|
175139
|
+
developerStatus: "pending",
|
|
175140
|
+
createdAt: now,
|
|
175141
|
+
updatedAt: now
|
|
175142
|
+
},
|
|
175143
|
+
unverifiedPlayer: {
|
|
175144
|
+
id: DEMO_USER_IDS.unverifiedPlayer,
|
|
175145
|
+
name: "Unverified Player",
|
|
175146
|
+
username: "unverified_player",
|
|
175147
|
+
email: "unverified@playcademy.com",
|
|
175148
|
+
emailVerified: false,
|
|
175149
|
+
image: null,
|
|
175150
|
+
role: "player",
|
|
175151
|
+
developerStatus: "none",
|
|
175152
|
+
createdAt: now,
|
|
175153
|
+
updatedAt: now
|
|
175154
|
+
}
|
|
175155
|
+
};
|
|
175156
|
+
var DEMO_USER = DEMO_USERS.player;
|
|
175157
|
+
var DEMO_TOKENS = {
|
|
175158
|
+
"sandbox-demo-token": DEMO_USERS.player,
|
|
175159
|
+
"sandbox-admin-token": DEMO_USERS.admin,
|
|
175160
|
+
"sandbox-player-token": DEMO_USERS.player,
|
|
175161
|
+
"sandbox-developer-token": DEMO_USERS.developer,
|
|
175162
|
+
"sandbox-pending-dev-token": DEMO_USERS.pendingDeveloper,
|
|
175163
|
+
"sandbox-unverified-token": DEMO_USERS.unverifiedPlayer,
|
|
175164
|
+
"mock-game-token-for-local-dev": DEMO_USERS.player
|
|
175165
|
+
};
|
|
175166
|
+
var DEMO_TOKEN = "sandbox-demo-token";
|
|
175167
|
+
var MOCK_GAME_ID = "mock-game-id-from-template";
|
|
175168
|
+
var DEMO_ITEM_IDS = {
|
|
175169
|
+
playcademyCredits: "10000000-0000-0000-0000-000000000001",
|
|
175170
|
+
foundingMemberBadge: "10000000-0000-0000-0000-000000000002",
|
|
175171
|
+
earlyAdopterBadge: "10000000-0000-0000-0000-000000000003",
|
|
175172
|
+
firstGameBadge: "10000000-0000-0000-0000-000000000004",
|
|
175173
|
+
commonSword: "10000000-0000-0000-0000-000000000005",
|
|
175174
|
+
smallHealthPotion: "10000000-0000-0000-0000-000000000006",
|
|
175175
|
+
smallBackpack: "10000000-0000-0000-0000-000000000007"
|
|
175176
|
+
};
|
|
175177
|
+
var PLAYCADEMY_CREDITS_ID = DEMO_ITEM_IDS.playcademyCredits;
|
|
175178
|
+
var SAMPLE_ITEMS = [
|
|
175179
|
+
{
|
|
175180
|
+
id: PLAYCADEMY_CREDITS_ID,
|
|
175181
|
+
slug: "PLAYCADEMY_CREDITS",
|
|
175182
|
+
gameId: null,
|
|
175183
|
+
displayName: "PLAYCADEMY credits",
|
|
175184
|
+
description: "The main currency used across PLAYCADEMY.",
|
|
175185
|
+
type: "currency",
|
|
175186
|
+
isPlaceable: false,
|
|
175187
|
+
imageUrl: "http://playcademy-sandbox.local/playcademy-credit.png",
|
|
175188
|
+
metadata: {
|
|
175189
|
+
rarity: "common"
|
|
175190
|
+
}
|
|
175191
|
+
},
|
|
175192
|
+
{
|
|
175193
|
+
id: DEMO_ITEM_IDS.foundingMemberBadge,
|
|
175194
|
+
slug: "FOUNDING_MEMBER_BADGE",
|
|
175195
|
+
gameId: null,
|
|
175196
|
+
displayName: "Founding Member Badge",
|
|
175197
|
+
description: "Reserved for founding core team of the PLAYCADEMY platform.",
|
|
175198
|
+
type: "badge",
|
|
175199
|
+
isPlaceable: false,
|
|
175200
|
+
imageUrl: null,
|
|
175201
|
+
metadata: {
|
|
175202
|
+
rarity: "legendary"
|
|
175203
|
+
}
|
|
175204
|
+
},
|
|
175205
|
+
{
|
|
175206
|
+
id: DEMO_ITEM_IDS.earlyAdopterBadge,
|
|
175207
|
+
slug: "EARLY_ADOPTER_BADGE",
|
|
175208
|
+
gameId: null,
|
|
175209
|
+
displayName: "Early Adopter Badge",
|
|
175210
|
+
description: "Awarded to users who joined during the beta phase.",
|
|
175211
|
+
type: "badge",
|
|
175212
|
+
isPlaceable: false,
|
|
175213
|
+
imageUrl: null,
|
|
175214
|
+
metadata: {
|
|
175215
|
+
rarity: "epic"
|
|
175216
|
+
}
|
|
175217
|
+
},
|
|
175218
|
+
{
|
|
175219
|
+
id: DEMO_ITEM_IDS.firstGameBadge,
|
|
175220
|
+
slug: "FIRST_GAME_BADGE",
|
|
175221
|
+
gameId: null,
|
|
175222
|
+
displayName: "First Game Played",
|
|
175223
|
+
description: "Awarded for playing your first game in the Playcademy platform.",
|
|
175224
|
+
type: "badge",
|
|
175225
|
+
isPlaceable: false,
|
|
175226
|
+
imageUrl: "http://playcademy-sandbox.local/first-game-badge.png",
|
|
175227
|
+
metadata: {
|
|
175228
|
+
rarity: "uncommon"
|
|
175229
|
+
}
|
|
175230
|
+
},
|
|
175231
|
+
{
|
|
175232
|
+
id: DEMO_ITEM_IDS.commonSword,
|
|
175233
|
+
slug: "COMMON_SWORD",
|
|
175234
|
+
gameId: null,
|
|
175235
|
+
displayName: "Common Sword",
|
|
175236
|
+
description: "A basic sword, good for beginners.",
|
|
175237
|
+
type: "unlock",
|
|
175238
|
+
isPlaceable: false,
|
|
175239
|
+
imageUrl: "http://playcademy-sandbox.local/common-sword.png",
|
|
175240
|
+
metadata: undefined
|
|
175241
|
+
},
|
|
175242
|
+
{
|
|
175243
|
+
id: DEMO_ITEM_IDS.smallHealthPotion,
|
|
175244
|
+
slug: "SMALL_HEALTH_POTION",
|
|
175245
|
+
gameId: null,
|
|
175246
|
+
displayName: "Small Health Potion",
|
|
175247
|
+
description: "Restores a small amount of health.",
|
|
175248
|
+
type: "other",
|
|
175249
|
+
isPlaceable: false,
|
|
175250
|
+
imageUrl: "http://playcademy-sandbox.local/small-health-potion.png",
|
|
175251
|
+
metadata: undefined
|
|
175252
|
+
},
|
|
175253
|
+
{
|
|
175254
|
+
id: DEMO_ITEM_IDS.smallBackpack,
|
|
175255
|
+
slug: "SMALL_BACKPACK",
|
|
175256
|
+
gameId: null,
|
|
175257
|
+
displayName: "Small Backpack",
|
|
175258
|
+
description: "Increases your inventory capacity by 5 slots.",
|
|
175259
|
+
type: "upgrade",
|
|
175260
|
+
isPlaceable: false,
|
|
175261
|
+
imageUrl: "http://playcademy-sandbox.local/small-backpack.png",
|
|
175262
|
+
metadata: undefined
|
|
175263
|
+
}
|
|
175264
|
+
];
|
|
175265
|
+
var SAMPLE_INVENTORY = [
|
|
175266
|
+
{
|
|
175267
|
+
id: "20000000-0000-0000-0000-000000000001",
|
|
175268
|
+
userId: DEMO_USER.id,
|
|
175269
|
+
itemId: PLAYCADEMY_CREDITS_ID,
|
|
175270
|
+
quantity: 1000
|
|
175271
|
+
}
|
|
175272
|
+
];
|
|
175152
175273
|
var RequestError = class extends Error {
|
|
175153
175274
|
constructor(message3, options) {
|
|
175154
175275
|
super(message3, options);
|
|
@@ -175684,9 +175805,99 @@ var serve = (options, listeningListener) => {
|
|
|
175684
175805
|
});
|
|
175685
175806
|
return server;
|
|
175686
175807
|
};
|
|
175808
|
+
function getRegistryPath() {
|
|
175809
|
+
const home = homedir();
|
|
175810
|
+
const dir = join2(home, ".playcademy");
|
|
175811
|
+
if (!existsSync2(dir)) {
|
|
175812
|
+
mkdirSync2(dir, { recursive: true });
|
|
175813
|
+
}
|
|
175814
|
+
return join2(dir, ".proc");
|
|
175815
|
+
}
|
|
175816
|
+
function readRegistry() {
|
|
175817
|
+
const registryPath = getRegistryPath();
|
|
175818
|
+
if (!existsSync2(registryPath)) {
|
|
175819
|
+
return {};
|
|
175820
|
+
}
|
|
175821
|
+
try {
|
|
175822
|
+
const content = readFileSync(registryPath, "utf-8");
|
|
175823
|
+
return JSON.parse(content);
|
|
175824
|
+
} catch {
|
|
175825
|
+
return {};
|
|
175826
|
+
}
|
|
175827
|
+
}
|
|
175828
|
+
function writeRegistry(registry) {
|
|
175829
|
+
const registryPath = getRegistryPath();
|
|
175830
|
+
writeFileSync(registryPath, JSON.stringify(registry, null, 2), "utf-8");
|
|
175831
|
+
}
|
|
175832
|
+
function getServerKey(type, port) {
|
|
175833
|
+
return `${type}-${port}`;
|
|
175834
|
+
}
|
|
175835
|
+
function writeServerInfo(type, info2) {
|
|
175836
|
+
const registry = readRegistry();
|
|
175837
|
+
const key = getServerKey(type, info2.port);
|
|
175838
|
+
registry[key] = info2;
|
|
175839
|
+
writeRegistry(registry);
|
|
175840
|
+
}
|
|
175841
|
+
function cleanupServerInfo(type, projectRoot, pid) {
|
|
175842
|
+
const registry = readRegistry();
|
|
175843
|
+
const keysToRemove = [];
|
|
175844
|
+
for (const [key, info2] of Object.entries(registry)) {
|
|
175845
|
+
if (key.startsWith(`${type}-`)) {
|
|
175846
|
+
let matches = true;
|
|
175847
|
+
if (projectRoot && info2.projectRoot !== projectRoot) {
|
|
175848
|
+
matches = false;
|
|
175849
|
+
}
|
|
175850
|
+
if (pid !== undefined && info2.pid !== pid) {
|
|
175851
|
+
matches = false;
|
|
175852
|
+
}
|
|
175853
|
+
if (matches) {
|
|
175854
|
+
keysToRemove.push(key);
|
|
175855
|
+
}
|
|
175856
|
+
}
|
|
175857
|
+
}
|
|
175858
|
+
for (const key of keysToRemove) {
|
|
175859
|
+
delete registry[key];
|
|
175860
|
+
}
|
|
175861
|
+
if (keysToRemove.length > 0) {
|
|
175862
|
+
writeRegistry(registry);
|
|
175863
|
+
}
|
|
175864
|
+
}
|
|
175865
|
+
async function isPortInUse(port) {
|
|
175866
|
+
return new Promise((resolve2) => {
|
|
175867
|
+
const server = createServer();
|
|
175868
|
+
server.once("error", () => {
|
|
175869
|
+
resolve2(true);
|
|
175870
|
+
});
|
|
175871
|
+
server.once("listening", () => {
|
|
175872
|
+
server.close();
|
|
175873
|
+
resolve2(false);
|
|
175874
|
+
});
|
|
175875
|
+
server.listen(port);
|
|
175876
|
+
});
|
|
175877
|
+
}
|
|
175878
|
+
async function waitForPort(port, timeoutMs = 5000) {
|
|
175879
|
+
const start2 = Date.now();
|
|
175880
|
+
while (await isPortInUse(port)) {
|
|
175881
|
+
if (Date.now() - start2 > timeoutMs) {
|
|
175882
|
+
throw new Error(`Port ${port} is already in use.
|
|
175883
|
+
Stop the other server or specify a different port with --port <number>.`);
|
|
175884
|
+
}
|
|
175885
|
+
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
175886
|
+
}
|
|
175887
|
+
}
|
|
175888
|
+
async function requirePortAvailable(port, timeoutMs = 100) {
|
|
175889
|
+
const start2 = Date.now();
|
|
175890
|
+
while (await isPortInUse(port)) {
|
|
175891
|
+
if (Date.now() - start2 > timeoutMs) {
|
|
175892
|
+
throw new Error(`Port ${port} is already in use.
|
|
175893
|
+
Stop the other server or specify a different port with --port <number>.`);
|
|
175894
|
+
}
|
|
175895
|
+
await new Promise((resolve2) => setTimeout(resolve2, 50));
|
|
175896
|
+
}
|
|
175897
|
+
}
|
|
175687
175898
|
var package_default = {
|
|
175688
175899
|
name: "@playcademy/sandbox",
|
|
175689
|
-
version: "0.
|
|
175900
|
+
version: "0.3.0",
|
|
175690
175901
|
description: "Local development server for Playcademy game development",
|
|
175691
175902
|
type: "module",
|
|
175692
175903
|
exports: {
|
|
@@ -175701,6 +175912,10 @@ var package_default = {
|
|
|
175701
175912
|
"./config": {
|
|
175702
175913
|
import: "./dist/config.js",
|
|
175703
175914
|
types: "./dist/config.d.ts"
|
|
175915
|
+
},
|
|
175916
|
+
"./constants": {
|
|
175917
|
+
import: "./dist/constants.js",
|
|
175918
|
+
types: "./dist/constants.d.ts"
|
|
175704
175919
|
}
|
|
175705
175920
|
},
|
|
175706
175921
|
bin: {
|
|
@@ -178130,7 +178345,7 @@ function sql(strings, ...params) {
|
|
|
178130
178345
|
return new SQL([new StringChunk(str)]);
|
|
178131
178346
|
}
|
|
178132
178347
|
sql22.raw = raw2;
|
|
178133
|
-
function
|
|
178348
|
+
function join22(chunks, separator) {
|
|
178134
178349
|
const result = [];
|
|
178135
178350
|
for (const [i22, chunk] of chunks.entries()) {
|
|
178136
178351
|
if (i22 > 0 && separator !== undefined) {
|
|
@@ -178140,7 +178355,7 @@ function sql(strings, ...params) {
|
|
|
178140
178355
|
}
|
|
178141
178356
|
return new SQL(result);
|
|
178142
178357
|
}
|
|
178143
|
-
sql22.join =
|
|
178358
|
+
sql22.join = join22;
|
|
178144
178359
|
function identifier(value) {
|
|
178145
178360
|
return new Name(value);
|
|
178146
178361
|
}
|
|
@@ -181053,7 +181268,7 @@ class PgSelectQueryBuilderBase extends TypedQueryBuilder {
|
|
|
181053
181268
|
return (table, on) => {
|
|
181054
181269
|
const baseTableName = this.tableName;
|
|
181055
181270
|
const tableName = getTableLikeName(table);
|
|
181056
|
-
if (typeof tableName === "string" && this.config.joins?.some((
|
|
181271
|
+
if (typeof tableName === "string" && this.config.joins?.some((join22) => join22.alias === tableName)) {
|
|
181057
181272
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
181058
181273
|
}
|
|
181059
181274
|
if (!this.isPartialSelect) {
|
|
@@ -181558,7 +181773,7 @@ class PgUpdateBase extends QueryPromise {
|
|
|
181558
181773
|
createJoin(joinType) {
|
|
181559
181774
|
return (table, on) => {
|
|
181560
181775
|
const tableName = getTableLikeName(table);
|
|
181561
|
-
if (typeof tableName === "string" && this.config.joins.some((
|
|
181776
|
+
if (typeof tableName === "string" && this.config.joins.some((join22) => join22.alias === tableName)) {
|
|
181562
181777
|
throw new Error(`Alias "${tableName}" is already used in this query`);
|
|
181563
181778
|
}
|
|
181564
181779
|
if (typeof on === "function") {
|
|
@@ -181608,10 +181823,10 @@ class PgUpdateBase extends QueryPromise {
|
|
|
181608
181823
|
const fromFields = this.getTableLikeFields(this.config.from);
|
|
181609
181824
|
fields[tableName] = fromFields;
|
|
181610
181825
|
}
|
|
181611
|
-
for (const
|
|
181612
|
-
const tableName2 = getTableLikeName(
|
|
181613
|
-
if (typeof tableName2 === "string" && !is(
|
|
181614
|
-
const fromFields = this.getTableLikeFields(
|
|
181826
|
+
for (const join22 of this.config.joins) {
|
|
181827
|
+
const tableName2 = getTableLikeName(join22.table);
|
|
181828
|
+
if (typeof tableName2 === "string" && !is(join22.table, SQL)) {
|
|
181829
|
+
const fromFields = this.getTableLikeFields(join22.table);
|
|
181615
181830
|
fields[tableName2] = fromFields;
|
|
181616
181831
|
}
|
|
181617
181832
|
}
|
|
@@ -182732,217 +182947,59 @@ var notificationsRelations = relations(notifications, ({ one }) => ({
|
|
|
182732
182947
|
references: [users.id]
|
|
182733
182948
|
})
|
|
182734
182949
|
}));
|
|
182735
|
-
|
|
182736
|
-
|
|
182737
|
-
|
|
182738
|
-
player: "00000000-0000-0000-0000-000000000002",
|
|
182739
|
-
developer: "00000000-0000-0000-0000-000000000003",
|
|
182740
|
-
pendingDeveloper: "00000000-0000-0000-0000-000000000004",
|
|
182741
|
-
unverifiedPlayer: "00000000-0000-0000-0000-000000000005"
|
|
182742
|
-
};
|
|
182743
|
-
var DEMO_USERS = {
|
|
182744
|
-
admin: {
|
|
182745
|
-
id: DEMO_USER_IDS.admin,
|
|
182746
|
-
name: "Admin User",
|
|
182747
|
-
username: "admin_user",
|
|
182748
|
-
email: "admin@playcademy.com",
|
|
182749
|
-
emailVerified: true,
|
|
182750
|
-
image: null,
|
|
182751
|
-
role: "admin",
|
|
182752
|
-
developerStatus: "approved",
|
|
182753
|
-
createdAt: now,
|
|
182754
|
-
updatedAt: now
|
|
182755
|
-
},
|
|
182756
|
-
player: {
|
|
182757
|
-
id: DEMO_USER_IDS.player,
|
|
182758
|
-
name: "Player User",
|
|
182759
|
-
username: "player_user",
|
|
182760
|
-
email: "player@playcademy.com",
|
|
182761
|
-
emailVerified: true,
|
|
182762
|
-
image: null,
|
|
182763
|
-
role: "player",
|
|
182764
|
-
developerStatus: "none",
|
|
182765
|
-
createdAt: now,
|
|
182766
|
-
updatedAt: now
|
|
182767
|
-
},
|
|
182768
|
-
developer: {
|
|
182769
|
-
id: DEMO_USER_IDS.developer,
|
|
182770
|
-
name: "Developer User",
|
|
182771
|
-
username: "developer_user",
|
|
182772
|
-
email: "developer@playcademy.com",
|
|
182773
|
-
emailVerified: true,
|
|
182774
|
-
image: null,
|
|
182775
|
-
role: "developer",
|
|
182776
|
-
developerStatus: "approved",
|
|
182777
|
-
createdAt: now,
|
|
182778
|
-
updatedAt: now
|
|
182779
|
-
},
|
|
182780
|
-
pendingDeveloper: {
|
|
182781
|
-
id: DEMO_USER_IDS.pendingDeveloper,
|
|
182782
|
-
name: "Pending Developer",
|
|
182783
|
-
username: "pending_dev",
|
|
182784
|
-
email: "pending@playcademy.com",
|
|
182785
|
-
emailVerified: true,
|
|
182786
|
-
image: null,
|
|
182787
|
-
role: "developer",
|
|
182788
|
-
developerStatus: "pending",
|
|
182789
|
-
createdAt: now,
|
|
182790
|
-
updatedAt: now
|
|
182791
|
-
},
|
|
182792
|
-
unverifiedPlayer: {
|
|
182793
|
-
id: DEMO_USER_IDS.unverifiedPlayer,
|
|
182794
|
-
name: "Unverified Player",
|
|
182795
|
-
username: "unverified_player",
|
|
182796
|
-
email: "unverified@playcademy.com",
|
|
182797
|
-
emailVerified: false,
|
|
182798
|
-
image: null,
|
|
182799
|
-
role: "player",
|
|
182800
|
-
developerStatus: "none",
|
|
182801
|
-
createdAt: now,
|
|
182802
|
-
updatedAt: now
|
|
182950
|
+
function extractBearerToken(authHeader) {
|
|
182951
|
+
if (!authHeader?.startsWith("Bearer ")) {
|
|
182952
|
+
return null;
|
|
182803
182953
|
}
|
|
182804
|
-
|
|
182805
|
-
|
|
182806
|
-
|
|
182807
|
-
|
|
182808
|
-
|
|
182809
|
-
|
|
182810
|
-
|
|
182811
|
-
"sandbox-pending-dev-token": DEMO_USERS.pendingDeveloper,
|
|
182812
|
-
"sandbox-unverified-token": DEMO_USERS.unverifiedPlayer,
|
|
182813
|
-
"mock-game-token-for-local-dev": DEMO_USERS.admin
|
|
182814
|
-
};
|
|
182815
|
-
var MOCK_GAME_ID = "mock-game-id-from-template";
|
|
182816
|
-
var DEMO_ITEM_IDS = {
|
|
182817
|
-
playcademyCredits: "10000000-0000-0000-0000-000000000001",
|
|
182818
|
-
foundingMemberBadge: "10000000-0000-0000-0000-000000000002",
|
|
182819
|
-
earlyAdopterBadge: "10000000-0000-0000-0000-000000000003",
|
|
182820
|
-
firstGameBadge: "10000000-0000-0000-0000-000000000004",
|
|
182821
|
-
commonSword: "10000000-0000-0000-0000-000000000005",
|
|
182822
|
-
smallHealthPotion: "10000000-0000-0000-0000-000000000006",
|
|
182823
|
-
smallBackpack: "10000000-0000-0000-0000-000000000007"
|
|
182824
|
-
};
|
|
182825
|
-
var PLAYCADEMY_CREDITS_ID = DEMO_ITEM_IDS.playcademyCredits;
|
|
182826
|
-
var SAMPLE_ITEMS = [
|
|
182827
|
-
{
|
|
182828
|
-
id: PLAYCADEMY_CREDITS_ID,
|
|
182829
|
-
slug: "PLAYCADEMY_CREDITS",
|
|
182830
|
-
gameId: null,
|
|
182831
|
-
displayName: "PLAYCADEMY credits",
|
|
182832
|
-
description: "The main currency used across PLAYCADEMY.",
|
|
182833
|
-
type: "currency",
|
|
182834
|
-
isPlaceable: false,
|
|
182835
|
-
imageUrl: "http://playcademy-sandbox.local/playcademy-credit.png",
|
|
182836
|
-
metadata: {
|
|
182837
|
-
rarity: "common"
|
|
182838
|
-
}
|
|
182839
|
-
},
|
|
182840
|
-
{
|
|
182841
|
-
id: DEMO_ITEM_IDS.foundingMemberBadge,
|
|
182842
|
-
slug: "FOUNDING_MEMBER_BADGE",
|
|
182843
|
-
gameId: null,
|
|
182844
|
-
displayName: "Founding Member Badge",
|
|
182845
|
-
description: "Reserved for founding core team of the PLAYCADEMY platform.",
|
|
182846
|
-
type: "badge",
|
|
182847
|
-
isPlaceable: false,
|
|
182848
|
-
imageUrl: null,
|
|
182849
|
-
metadata: {
|
|
182850
|
-
rarity: "legendary"
|
|
182954
|
+
return authHeader.substring(7);
|
|
182955
|
+
}
|
|
182956
|
+
function parseSandboxToken(token) {
|
|
182957
|
+
try {
|
|
182958
|
+
const parts2 = token.split(".");
|
|
182959
|
+
if (parts2.length !== 3 || parts2[2] !== "sandbox") {
|
|
182960
|
+
return null;
|
|
182851
182961
|
}
|
|
182852
|
-
|
|
182853
|
-
|
|
182854
|
-
|
|
182855
|
-
slug: "EARLY_ADOPTER_BADGE",
|
|
182856
|
-
gameId: null,
|
|
182857
|
-
displayName: "Early Adopter Badge",
|
|
182858
|
-
description: "Awarded to users who joined during the beta phase.",
|
|
182859
|
-
type: "badge",
|
|
182860
|
-
isPlaceable: false,
|
|
182861
|
-
imageUrl: null,
|
|
182862
|
-
metadata: {
|
|
182863
|
-
rarity: "epic"
|
|
182962
|
+
const header = JSON.parse(atob(parts2[0]));
|
|
182963
|
+
if (header.typ !== "sandbox") {
|
|
182964
|
+
return null;
|
|
182864
182965
|
}
|
|
182865
|
-
|
|
182866
|
-
|
|
182867
|
-
|
|
182868
|
-
slug: "FIRST_GAME_BADGE",
|
|
182869
|
-
gameId: null,
|
|
182870
|
-
displayName: "First Game Played",
|
|
182871
|
-
description: "Awarded for playing your first game in the Playcademy platform.",
|
|
182872
|
-
type: "badge",
|
|
182873
|
-
isPlaceable: false,
|
|
182874
|
-
imageUrl: "http://playcademy-sandbox.local/first-game-badge.png",
|
|
182875
|
-
metadata: {
|
|
182876
|
-
rarity: "uncommon"
|
|
182966
|
+
const payload = JSON.parse(atob(parts2[1]));
|
|
182967
|
+
if (!payload.uid || !payload.sub) {
|
|
182968
|
+
return null;
|
|
182877
182969
|
}
|
|
182878
|
-
|
|
182879
|
-
|
|
182880
|
-
|
|
182881
|
-
|
|
182882
|
-
|
|
182883
|
-
displayName: "Common Sword",
|
|
182884
|
-
description: "A basic sword, good for beginners.",
|
|
182885
|
-
type: "unlock",
|
|
182886
|
-
isPlaceable: false,
|
|
182887
|
-
imageUrl: "http://playcademy-sandbox.local/common-sword.png",
|
|
182888
|
-
metadata: undefined
|
|
182889
|
-
},
|
|
182890
|
-
{
|
|
182891
|
-
id: DEMO_ITEM_IDS.smallHealthPotion,
|
|
182892
|
-
slug: "SMALL_HEALTH_POTION",
|
|
182893
|
-
gameId: null,
|
|
182894
|
-
displayName: "Small Health Potion",
|
|
182895
|
-
description: "Restores a small amount of health.",
|
|
182896
|
-
type: "other",
|
|
182897
|
-
isPlaceable: false,
|
|
182898
|
-
imageUrl: "http://playcademy-sandbox.local/small-health-potion.png",
|
|
182899
|
-
metadata: undefined
|
|
182900
|
-
},
|
|
182901
|
-
{
|
|
182902
|
-
id: DEMO_ITEM_IDS.smallBackpack,
|
|
182903
|
-
slug: "SMALL_BACKPACK",
|
|
182904
|
-
gameId: null,
|
|
182905
|
-
displayName: "Small Backpack",
|
|
182906
|
-
description: "Increases your inventory capacity by 5 slots.",
|
|
182907
|
-
type: "upgrade",
|
|
182908
|
-
isPlaceable: false,
|
|
182909
|
-
imageUrl: "http://playcademy-sandbox.local/small-backpack.png",
|
|
182910
|
-
metadata: undefined
|
|
182911
|
-
}
|
|
182912
|
-
];
|
|
182913
|
-
var SAMPLE_INVENTORY = [
|
|
182914
|
-
{
|
|
182915
|
-
id: "20000000-0000-0000-0000-000000000001",
|
|
182916
|
-
userId: DEMO_USER.id,
|
|
182917
|
-
itemId: PLAYCADEMY_CREDITS_ID,
|
|
182918
|
-
quantity: 1000
|
|
182919
|
-
}
|
|
182920
|
-
];
|
|
182921
|
-
function extractBearerToken(authHeader) {
|
|
182922
|
-
if (!authHeader?.startsWith("Bearer ")) {
|
|
182970
|
+
return {
|
|
182971
|
+
userId: payload.uid,
|
|
182972
|
+
gameSlug: payload.sub
|
|
182973
|
+
};
|
|
182974
|
+
} catch {
|
|
182923
182975
|
return null;
|
|
182924
182976
|
}
|
|
182925
|
-
return authHeader.substring(7);
|
|
182926
182977
|
}
|
|
182927
|
-
function
|
|
182978
|
+
function parseJwtClaims(token) {
|
|
182928
182979
|
try {
|
|
182929
182980
|
const parts2 = token.split(".");
|
|
182930
182981
|
if (parts2.length === 3 && parts2[1]) {
|
|
182931
182982
|
const payload = JSON.parse(atob(parts2[1]));
|
|
182932
|
-
|
|
182983
|
+
if (payload.uid) {
|
|
182984
|
+
return {
|
|
182985
|
+
userId: payload.uid,
|
|
182986
|
+
gameId: payload.sub
|
|
182987
|
+
};
|
|
182988
|
+
}
|
|
182933
182989
|
}
|
|
182934
|
-
} catch
|
|
182935
|
-
console.warn("[Auth] Failed to decode JWT token:", error2);
|
|
182936
|
-
}
|
|
182990
|
+
} catch {}
|
|
182937
182991
|
return null;
|
|
182938
182992
|
}
|
|
182939
|
-
function
|
|
182993
|
+
function resolveAuth(token) {
|
|
182940
182994
|
const demoUser = DEMO_TOKENS[token];
|
|
182941
|
-
if (demoUser)
|
|
182942
|
-
return demoUser.id;
|
|
182943
|
-
}
|
|
182995
|
+
if (demoUser)
|
|
182996
|
+
return { userId: demoUser.id };
|
|
182944
182997
|
if (token.includes(".")) {
|
|
182945
|
-
|
|
182998
|
+
const sandboxClaims = parseSandboxToken(token);
|
|
182999
|
+
if (sandboxClaims) {
|
|
183000
|
+
return sandboxClaims;
|
|
183001
|
+
}
|
|
183002
|
+
return parseJwtClaims(token);
|
|
182946
183003
|
}
|
|
182947
183004
|
return null;
|
|
182948
183005
|
}
|
|
@@ -182957,6 +183014,18 @@ async function fetchUserFromDatabase(db, userId) {
|
|
|
182957
183014
|
throw error2;
|
|
182958
183015
|
}
|
|
182959
183016
|
}
|
|
183017
|
+
async function resolveGameIdFromSlug(db, slug) {
|
|
183018
|
+
try {
|
|
183019
|
+
const game = await db.query.games.findFirst({
|
|
183020
|
+
where: eq(games.slug, slug),
|
|
183021
|
+
columns: { id: true }
|
|
183022
|
+
});
|
|
183023
|
+
return game?.id || null;
|
|
183024
|
+
} catch (error2) {
|
|
183025
|
+
console.error("[Auth] Error looking up game by slug:", error2);
|
|
183026
|
+
return null;
|
|
183027
|
+
}
|
|
183028
|
+
}
|
|
182960
183029
|
function isPublicRoute(path4, exceptions) {
|
|
182961
183030
|
return exceptions.some((exception) => {
|
|
182962
183031
|
if (path4 === exception)
|
|
@@ -182978,11 +183047,11 @@ async function authenticateRequest(c3) {
|
|
|
182978
183047
|
shouldReturn404: true
|
|
182979
183048
|
};
|
|
182980
183049
|
}
|
|
182981
|
-
let
|
|
183050
|
+
let claims;
|
|
182982
183051
|
if (apiKey && !bearerToken) {
|
|
182983
|
-
|
|
183052
|
+
claims = { userId: DEMO_USERS.admin.id };
|
|
182984
183053
|
} else {
|
|
182985
|
-
const resolved =
|
|
183054
|
+
const resolved = resolveAuth(token);
|
|
182986
183055
|
if (!resolved) {
|
|
182987
183056
|
return {
|
|
182988
183057
|
success: false,
|
|
@@ -182990,26 +183059,22 @@ async function authenticateRequest(c3) {
|
|
|
182990
183059
|
shouldReturn404: true
|
|
182991
183060
|
};
|
|
182992
183061
|
}
|
|
182993
|
-
|
|
183062
|
+
claims = resolved;
|
|
182994
183063
|
}
|
|
182995
183064
|
const db = c3.get("db");
|
|
182996
183065
|
if (!db) {
|
|
182997
183066
|
console.error("[Auth] Database not available in context");
|
|
182998
|
-
return {
|
|
182999
|
-
success: false,
|
|
183000
|
-
error: "Internal server error",
|
|
183001
|
-
shouldReturn404: false
|
|
183002
|
-
};
|
|
183067
|
+
return { success: false, error: "Internal server error", shouldReturn404: false };
|
|
183003
183068
|
}
|
|
183004
|
-
const user = await fetchUserFromDatabase(db,
|
|
183069
|
+
const user = await fetchUserFromDatabase(db, claims.userId);
|
|
183005
183070
|
if (!user) {
|
|
183006
|
-
return {
|
|
183007
|
-
success: false,
|
|
183008
|
-
error: "User not found or token invalid",
|
|
183009
|
-
shouldReturn404: true
|
|
183010
|
-
};
|
|
183071
|
+
return { success: false, error: "User not found or token invalid", shouldReturn404: true };
|
|
183011
183072
|
}
|
|
183012
|
-
|
|
183073
|
+
let gameId = claims.gameId;
|
|
183074
|
+
if (!gameId && claims.gameSlug) {
|
|
183075
|
+
gameId = await resolveGameIdFromSlug(db, claims.gameSlug) ?? undefined;
|
|
183076
|
+
}
|
|
183077
|
+
return { success: true, user, gameId };
|
|
183013
183078
|
}
|
|
183014
183079
|
function setupAuth(options = {}) {
|
|
183015
183080
|
const { exceptions = [] } = options;
|
|
@@ -183021,6 +183086,8 @@ function setupAuth(options = {}) {
|
|
|
183021
183086
|
const result = await authenticateRequest(c3);
|
|
183022
183087
|
if (result.success) {
|
|
183023
183088
|
c3.set("user", result.user);
|
|
183089
|
+
if (result.gameId)
|
|
183090
|
+
c3.set("gameId", result.gameId);
|
|
183024
183091
|
await next();
|
|
183025
183092
|
return;
|
|
183026
183093
|
}
|
|
@@ -188861,31 +188928,31 @@ function getDatabase() {
|
|
|
188861
188928
|
}
|
|
188862
188929
|
|
|
188863
188930
|
class DatabasePathManager {
|
|
188864
|
-
static DEFAULT_DB_SUBPATH =
|
|
188931
|
+
static DEFAULT_DB_SUBPATH = join3("@playcademy", "vite-plugin", "node_modules", ".playcademy", "sandbox.db");
|
|
188865
188932
|
static findNodeModulesPath() {
|
|
188866
188933
|
let currentDir = process.cwd();
|
|
188867
188934
|
while (currentDir !== dirname(currentDir)) {
|
|
188868
|
-
const nodeModulesPath =
|
|
188935
|
+
const nodeModulesPath = join3(currentDir, "node_modules");
|
|
188869
188936
|
if (fs22.existsSync(nodeModulesPath)) {
|
|
188870
188937
|
return nodeModulesPath;
|
|
188871
188938
|
}
|
|
188872
188939
|
currentDir = dirname(currentDir);
|
|
188873
188940
|
}
|
|
188874
|
-
return
|
|
188941
|
+
return join3(process.cwd(), "node_modules");
|
|
188875
188942
|
}
|
|
188876
188943
|
static resolveDatabasePath(customPath) {
|
|
188877
188944
|
if (customPath) {
|
|
188878
188945
|
if (customPath === ":memory:")
|
|
188879
188946
|
return ":memory:";
|
|
188880
|
-
return isAbsolute(customPath) ? customPath :
|
|
188947
|
+
return isAbsolute(customPath) ? customPath : join3(process.cwd(), customPath);
|
|
188881
188948
|
}
|
|
188882
|
-
return
|
|
188949
|
+
return join3(this.findNodeModulesPath(), this.DEFAULT_DB_SUBPATH);
|
|
188883
188950
|
}
|
|
188884
188951
|
static ensureDatabaseDirectory(dbPath) {
|
|
188885
188952
|
if (dbPath === ":memory:")
|
|
188886
188953
|
return;
|
|
188887
188954
|
const dirPath = dirname(dbPath);
|
|
188888
|
-
const absolutePath = isAbsolute(dirPath) ? dirPath :
|
|
188955
|
+
const absolutePath = isAbsolute(dirPath) ? dirPath : join3(process.cwd(), dirPath);
|
|
188889
188956
|
try {
|
|
188890
188957
|
if (!fs22.existsSync(absolutePath)) {
|
|
188891
188958
|
fs22.mkdirSync(absolutePath, { recursive: true });
|
|
@@ -189196,7 +189263,51 @@ var init_overworld = __esm3(() => {
|
|
|
189196
189263
|
FIRST_GAME: ITEM_SLUGS2.FIRST_GAME_BADGE
|
|
189197
189264
|
};
|
|
189198
189265
|
});
|
|
189199
|
-
var
|
|
189266
|
+
var TIMEBACK_ORG_SOURCED_ID = "PLAYCADEMY";
|
|
189267
|
+
var TIMEBACK_ORG_NAME = "Playcademy Studios";
|
|
189268
|
+
var TIMEBACK_ORG_TYPE = "department";
|
|
189269
|
+
var TIMEBACK_COURSE_DEFAULTS;
|
|
189270
|
+
var TIMEBACK_RESOURCE_DEFAULTS;
|
|
189271
|
+
var TIMEBACK_COMPONENT_DEFAULTS;
|
|
189272
|
+
var TIMEBACK_COMPONENT_RESOURCE_DEFAULTS;
|
|
189273
|
+
var init_timeback = __esm3(() => {
|
|
189274
|
+
TIMEBACK_COURSE_DEFAULTS = {
|
|
189275
|
+
gradingScheme: "STANDARD",
|
|
189276
|
+
level: {
|
|
189277
|
+
elementary: "Elementary",
|
|
189278
|
+
middle: "Middle",
|
|
189279
|
+
high: "High",
|
|
189280
|
+
ap: "AP"
|
|
189281
|
+
},
|
|
189282
|
+
goals: {
|
|
189283
|
+
dailyXp: 50,
|
|
189284
|
+
dailyLessons: 3
|
|
189285
|
+
},
|
|
189286
|
+
metrics: {
|
|
189287
|
+
totalXp: 1000,
|
|
189288
|
+
totalLessons: 50
|
|
189289
|
+
}
|
|
189290
|
+
};
|
|
189291
|
+
TIMEBACK_RESOURCE_DEFAULTS = {
|
|
189292
|
+
vendorId: "playcademy",
|
|
189293
|
+
roles: ["primary"],
|
|
189294
|
+
importance: "primary",
|
|
189295
|
+
metadata: {
|
|
189296
|
+
type: "interactive",
|
|
189297
|
+
toolProvider: "Playcademy",
|
|
189298
|
+
instructionalMethod: "exploratory",
|
|
189299
|
+
language: "en-US"
|
|
189300
|
+
}
|
|
189301
|
+
};
|
|
189302
|
+
TIMEBACK_COMPONENT_DEFAULTS = {
|
|
189303
|
+
sortOrder: 1,
|
|
189304
|
+
prerequisiteCriteria: "ALL"
|
|
189305
|
+
};
|
|
189306
|
+
TIMEBACK_COMPONENT_RESOURCE_DEFAULTS = {
|
|
189307
|
+
sortOrder: 1,
|
|
189308
|
+
lessonType: "quiz"
|
|
189309
|
+
};
|
|
189310
|
+
});
|
|
189200
189311
|
var init_workers = () => {};
|
|
189201
189312
|
var init_src2 = __esm3(() => {
|
|
189202
189313
|
init_auth();
|
|
@@ -189237,7 +189348,6 @@ var HTTP_DEFAULTS;
|
|
|
189237
189348
|
var AUTH_DEFAULTS;
|
|
189238
189349
|
var CACHE_DEFAULTS;
|
|
189239
189350
|
var CONFIG_DEFAULTS;
|
|
189240
|
-
var DEFAULT_PLAYCADEMY_ORGANIZATION_ID;
|
|
189241
189351
|
var PLAYCADEMY_DEFAULTS;
|
|
189242
189352
|
var RESOURCE_DEFAULTS;
|
|
189243
189353
|
var HTTP_STATUS;
|
|
@@ -189384,54 +189494,26 @@ var init_constants = __esm3(() => {
|
|
|
189384
189494
|
CONFIG_DEFAULTS = {
|
|
189385
189495
|
fileNames: ["timeback.config.js", "timeback.config.json"]
|
|
189386
189496
|
};
|
|
189387
|
-
DEFAULT_PLAYCADEMY_ORGANIZATION_ID = process.env.TIMEBACK_ORG_SOURCE_ID || "PLAYCADEMY";
|
|
189388
189497
|
PLAYCADEMY_DEFAULTS = {
|
|
189389
|
-
organization:
|
|
189498
|
+
organization: TIMEBACK_ORG_SOURCED_ID,
|
|
189390
189499
|
launchBaseUrls: PLAYCADEMY_BASE_URLS
|
|
189391
189500
|
};
|
|
189392
189501
|
RESOURCE_DEFAULTS = {
|
|
189393
189502
|
organization: {
|
|
189394
|
-
name:
|
|
189395
|
-
type:
|
|
189503
|
+
name: TIMEBACK_ORG_NAME,
|
|
189504
|
+
type: TIMEBACK_ORG_TYPE
|
|
189396
189505
|
},
|
|
189397
189506
|
course: {
|
|
189398
|
-
gradingScheme:
|
|
189399
|
-
level:
|
|
189400
|
-
elementary: "Elementary",
|
|
189401
|
-
middle: "Middle",
|
|
189402
|
-
high: "High",
|
|
189403
|
-
ap: "AP"
|
|
189404
|
-
},
|
|
189405
|
-
metadata: {
|
|
189406
|
-
goals: {
|
|
189407
|
-
dailyXp: 50,
|
|
189408
|
-
dailyLessons: 3
|
|
189409
|
-
},
|
|
189410
|
-
metrics: {
|
|
189411
|
-
totalXp: 1000,
|
|
189412
|
-
totalLessons: 50
|
|
189413
|
-
}
|
|
189414
|
-
}
|
|
189415
|
-
},
|
|
189416
|
-
component: {
|
|
189417
|
-
sortOrder: 1,
|
|
189418
|
-
prerequisiteCriteria: "ALL"
|
|
189419
|
-
},
|
|
189420
|
-
resource: {
|
|
189421
|
-
vendorId: "playcademy",
|
|
189422
|
-
roles: ["primary"],
|
|
189423
|
-
importance: "primary",
|
|
189507
|
+
gradingScheme: TIMEBACK_COURSE_DEFAULTS.gradingScheme,
|
|
189508
|
+
level: TIMEBACK_COURSE_DEFAULTS.level,
|
|
189424
189509
|
metadata: {
|
|
189425
|
-
|
|
189426
|
-
|
|
189427
|
-
instructionalMethod: "exploratory",
|
|
189428
|
-
language: "en-US"
|
|
189510
|
+
goals: TIMEBACK_COURSE_DEFAULTS.goals,
|
|
189511
|
+
metrics: TIMEBACK_COURSE_DEFAULTS.metrics
|
|
189429
189512
|
}
|
|
189430
189513
|
},
|
|
189431
|
-
|
|
189432
|
-
|
|
189433
|
-
|
|
189434
|
-
}
|
|
189514
|
+
component: TIMEBACK_COMPONENT_DEFAULTS,
|
|
189515
|
+
resource: TIMEBACK_RESOURCE_DEFAULTS,
|
|
189516
|
+
componentResource: TIMEBACK_COMPONENT_RESOURCE_DEFAULTS
|
|
189435
189517
|
};
|
|
189436
189518
|
HTTP_STATUS = {
|
|
189437
189519
|
CLIENT_ERROR_MIN: 400,
|
|
@@ -195440,7 +195522,8 @@ class TimebackClient {
|
|
|
195440
195522
|
courseId: enrollment.course.id,
|
|
195441
195523
|
status: "active",
|
|
195442
195524
|
grades,
|
|
195443
|
-
subjects
|
|
195525
|
+
subjects,
|
|
195526
|
+
school: enrollment.school
|
|
195444
195527
|
};
|
|
195445
195528
|
});
|
|
195446
195529
|
this.cacheManager.setEnrollments(studentId, enrollments);
|
|
@@ -195511,7 +195594,51 @@ var init_overworld2 = __esm4(() => {
|
|
|
195511
195594
|
FIRST_GAME: ITEM_SLUGS3.FIRST_GAME_BADGE
|
|
195512
195595
|
};
|
|
195513
195596
|
});
|
|
195514
|
-
var
|
|
195597
|
+
var TIMEBACK_ORG_SOURCED_ID2 = "PLAYCADEMY";
|
|
195598
|
+
var TIMEBACK_ORG_NAME2 = "Playcademy Studios";
|
|
195599
|
+
var TIMEBACK_ORG_TYPE2 = "department";
|
|
195600
|
+
var TIMEBACK_COURSE_DEFAULTS2;
|
|
195601
|
+
var TIMEBACK_RESOURCE_DEFAULTS2;
|
|
195602
|
+
var TIMEBACK_COMPONENT_DEFAULTS2;
|
|
195603
|
+
var TIMEBACK_COMPONENT_RESOURCE_DEFAULTS2;
|
|
195604
|
+
var init_timeback2 = __esm4(() => {
|
|
195605
|
+
TIMEBACK_COURSE_DEFAULTS2 = {
|
|
195606
|
+
gradingScheme: "STANDARD",
|
|
195607
|
+
level: {
|
|
195608
|
+
elementary: "Elementary",
|
|
195609
|
+
middle: "Middle",
|
|
195610
|
+
high: "High",
|
|
195611
|
+
ap: "AP"
|
|
195612
|
+
},
|
|
195613
|
+
goals: {
|
|
195614
|
+
dailyXp: 50,
|
|
195615
|
+
dailyLessons: 3
|
|
195616
|
+
},
|
|
195617
|
+
metrics: {
|
|
195618
|
+
totalXp: 1000,
|
|
195619
|
+
totalLessons: 50
|
|
195620
|
+
}
|
|
195621
|
+
};
|
|
195622
|
+
TIMEBACK_RESOURCE_DEFAULTS2 = {
|
|
195623
|
+
vendorId: "playcademy",
|
|
195624
|
+
roles: ["primary"],
|
|
195625
|
+
importance: "primary",
|
|
195626
|
+
metadata: {
|
|
195627
|
+
type: "interactive",
|
|
195628
|
+
toolProvider: "Playcademy",
|
|
195629
|
+
instructionalMethod: "exploratory",
|
|
195630
|
+
language: "en-US"
|
|
195631
|
+
}
|
|
195632
|
+
};
|
|
195633
|
+
TIMEBACK_COMPONENT_DEFAULTS2 = {
|
|
195634
|
+
sortOrder: 1,
|
|
195635
|
+
prerequisiteCriteria: "ALL"
|
|
195636
|
+
};
|
|
195637
|
+
TIMEBACK_COMPONENT_RESOURCE_DEFAULTS2 = {
|
|
195638
|
+
sortOrder: 1,
|
|
195639
|
+
lessonType: "quiz"
|
|
195640
|
+
};
|
|
195641
|
+
});
|
|
195515
195642
|
var init_workers2 = () => {};
|
|
195516
195643
|
var init_src3 = __esm4(() => {
|
|
195517
195644
|
init_auth2();
|
|
@@ -195543,7 +195670,6 @@ var HTTP_DEFAULTS2;
|
|
|
195543
195670
|
var AUTH_DEFAULTS2;
|
|
195544
195671
|
var CACHE_DEFAULTS2;
|
|
195545
195672
|
var CONFIG_DEFAULTS2;
|
|
195546
|
-
var DEFAULT_PLAYCADEMY_ORGANIZATION_ID2;
|
|
195547
195673
|
var PLAYCADEMY_DEFAULTS2;
|
|
195548
195674
|
var RESOURCE_DEFAULTS2;
|
|
195549
195675
|
var HTTP_STATUS2;
|
|
@@ -195690,54 +195816,26 @@ var init_constants2 = __esm4(() => {
|
|
|
195690
195816
|
CONFIG_DEFAULTS2 = {
|
|
195691
195817
|
fileNames: ["timeback.config.js", "timeback.config.json"]
|
|
195692
195818
|
};
|
|
195693
|
-
DEFAULT_PLAYCADEMY_ORGANIZATION_ID2 = process.env.TIMEBACK_ORG_SOURCE_ID || "PLAYCADEMY";
|
|
195694
195819
|
PLAYCADEMY_DEFAULTS2 = {
|
|
195695
|
-
organization:
|
|
195820
|
+
organization: TIMEBACK_ORG_SOURCED_ID2,
|
|
195696
195821
|
launchBaseUrls: PLAYCADEMY_BASE_URLS2
|
|
195697
195822
|
};
|
|
195698
195823
|
RESOURCE_DEFAULTS2 = {
|
|
195699
195824
|
organization: {
|
|
195700
|
-
name:
|
|
195701
|
-
type:
|
|
195825
|
+
name: TIMEBACK_ORG_NAME2,
|
|
195826
|
+
type: TIMEBACK_ORG_TYPE2
|
|
195702
195827
|
},
|
|
195703
195828
|
course: {
|
|
195704
|
-
gradingScheme:
|
|
195705
|
-
level:
|
|
195706
|
-
elementary: "Elementary",
|
|
195707
|
-
middle: "Middle",
|
|
195708
|
-
high: "High",
|
|
195709
|
-
ap: "AP"
|
|
195710
|
-
},
|
|
195711
|
-
metadata: {
|
|
195712
|
-
goals: {
|
|
195713
|
-
dailyXp: 50,
|
|
195714
|
-
dailyLessons: 3
|
|
195715
|
-
},
|
|
195716
|
-
metrics: {
|
|
195717
|
-
totalXp: 1000,
|
|
195718
|
-
totalLessons: 50
|
|
195719
|
-
}
|
|
195720
|
-
}
|
|
195721
|
-
},
|
|
195722
|
-
component: {
|
|
195723
|
-
sortOrder: 1,
|
|
195724
|
-
prerequisiteCriteria: "ALL"
|
|
195725
|
-
},
|
|
195726
|
-
resource: {
|
|
195727
|
-
vendorId: "playcademy",
|
|
195728
|
-
roles: ["primary"],
|
|
195729
|
-
importance: "primary",
|
|
195829
|
+
gradingScheme: TIMEBACK_COURSE_DEFAULTS2.gradingScheme,
|
|
195830
|
+
level: TIMEBACK_COURSE_DEFAULTS2.level,
|
|
195730
195831
|
metadata: {
|
|
195731
|
-
|
|
195732
|
-
|
|
195733
|
-
instructionalMethod: "exploratory",
|
|
195734
|
-
language: "en-US"
|
|
195832
|
+
goals: TIMEBACK_COURSE_DEFAULTS2.goals,
|
|
195833
|
+
metrics: TIMEBACK_COURSE_DEFAULTS2.metrics
|
|
195735
195834
|
}
|
|
195736
195835
|
},
|
|
195737
|
-
|
|
195738
|
-
|
|
195739
|
-
|
|
195740
|
-
}
|
|
195836
|
+
component: TIMEBACK_COMPONENT_DEFAULTS2,
|
|
195837
|
+
resource: TIMEBACK_RESOURCE_DEFAULTS2,
|
|
195838
|
+
componentResource: TIMEBACK_COMPONENT_RESOURCE_DEFAULTS2
|
|
195741
195839
|
};
|
|
195742
195840
|
HTTP_STATUS2 = {
|
|
195743
195841
|
CLIENT_ERROR_MIN: 400,
|
|
@@ -196079,66 +196177,87 @@ function buildResourceMetadata({
|
|
|
196079
196177
|
return metadata2;
|
|
196080
196178
|
}
|
|
196081
196179
|
init_src();
|
|
196082
|
-
async function
|
|
196083
|
-
|
|
196084
|
-
|
|
196085
|
-
|
|
196086
|
-
const
|
|
196087
|
-
|
|
196088
|
-
|
|
196089
|
-
|
|
196090
|
-
|
|
196091
|
-
|
|
196092
|
-
|
|
196180
|
+
async function fetchStudentFromOneRoster(timebackId) {
|
|
196181
|
+
log2.debug("[OneRoster] Fetching student profile", { timebackId });
|
|
196182
|
+
try {
|
|
196183
|
+
const client2 = await getTimebackClient();
|
|
196184
|
+
const user = await client2.oneroster.users.get(timebackId);
|
|
196185
|
+
const primaryRoleEntry = user.roles.find((r22) => r22.roleType === "primary");
|
|
196186
|
+
const role = primaryRoleEntry?.role ?? user.roles[0]?.role ?? "student";
|
|
196187
|
+
const orgMap = new Map;
|
|
196188
|
+
if (user.primaryOrg) {
|
|
196189
|
+
orgMap.set(user.primaryOrg.sourcedId, {
|
|
196190
|
+
id: user.primaryOrg.sourcedId,
|
|
196191
|
+
name: user.primaryOrg.name ?? null,
|
|
196192
|
+
type: user.primaryOrg.type || "school",
|
|
196193
|
+
isPrimary: true
|
|
196194
|
+
});
|
|
196195
|
+
}
|
|
196196
|
+
for (const r22 of user.roles) {
|
|
196197
|
+
if (r22.org && !orgMap.has(r22.org.sourcedId)) {
|
|
196198
|
+
orgMap.set(r22.org.sourcedId, {
|
|
196199
|
+
id: r22.org.sourcedId,
|
|
196200
|
+
name: null,
|
|
196201
|
+
type: "school",
|
|
196202
|
+
isPrimary: false
|
|
196203
|
+
});
|
|
196204
|
+
}
|
|
196205
|
+
}
|
|
196206
|
+
const organizations = Array.from(orgMap.values());
|
|
196207
|
+
return { role, organizations };
|
|
196208
|
+
} catch (error2) {
|
|
196209
|
+
log2.warn("[OneRoster] Failed to fetch student, using defaults", { error: error2, timebackId });
|
|
196210
|
+
return { role: "student", organizations: [] };
|
|
196093
196211
|
}
|
|
196094
|
-
|
|
196212
|
+
}
|
|
196213
|
+
async function fetchEnrollmentsFromEduBridge(timebackId) {
|
|
196214
|
+
const db = getDatabase();
|
|
196215
|
+
log2.debug("[EduBridge] Fetching student enrollments", { timebackId });
|
|
196095
196216
|
try {
|
|
196096
196217
|
const client2 = await getTimebackClient();
|
|
196097
|
-
const
|
|
196098
|
-
const courseIds =
|
|
196099
|
-
if (courseIds.length === 0)
|
|
196218
|
+
const enrollments = await client2.getEnrollments(timebackId);
|
|
196219
|
+
const courseIds = enrollments.map((e2) => e2.courseId).filter((id) => Boolean(id));
|
|
196220
|
+
if (courseIds.length === 0)
|
|
196100
196221
|
return [];
|
|
196101
|
-
|
|
196222
|
+
const courseToSchool = new Map(enrollments.filter((e2) => e2.school?.id).map((e2) => [e2.courseId, e2.school.id]));
|
|
196102
196223
|
const integrations = await db.query.gameTimebackIntegrations.findMany({
|
|
196103
196224
|
where: inArray(gameTimebackIntegrations.courseId, courseIds)
|
|
196104
196225
|
});
|
|
196105
|
-
return integrations.map((
|
|
196106
|
-
gameId:
|
|
196107
|
-
grade:
|
|
196108
|
-
subject:
|
|
196109
|
-
courseId:
|
|
196226
|
+
return integrations.map((i32) => ({
|
|
196227
|
+
gameId: i32.gameId,
|
|
196228
|
+
grade: i32.grade,
|
|
196229
|
+
subject: i32.subject,
|
|
196230
|
+
courseId: i32.courseId,
|
|
196231
|
+
orgId: courseToSchool.get(i32.courseId)
|
|
196110
196232
|
}));
|
|
196111
196233
|
} catch (error2) {
|
|
196112
|
-
log2.warn("[
|
|
196113
|
-
error: error2,
|
|
196114
|
-
timebackId
|
|
196115
|
-
});
|
|
196234
|
+
log2.warn("[EduBridge] Failed to fetch enrollments", { error: error2, timebackId });
|
|
196116
196235
|
return [];
|
|
196117
196236
|
}
|
|
196118
196237
|
}
|
|
196119
|
-
|
|
196120
|
-
|
|
196121
|
-
try {
|
|
196122
|
-
const client2 = await getTimebackClient();
|
|
196123
|
-
const user = await client2.oneroster.users.get(timebackId);
|
|
196124
|
-
const primaryRole = user.roles.find((r22) => r22.roleType === "primary");
|
|
196125
|
-
const role = primaryRole?.role ?? user.roles[0]?.role ?? "student";
|
|
196126
|
-
log2.debug("[timeback] Resolved user role", { timebackId, role });
|
|
196127
|
-
return role;
|
|
196128
|
-
} catch (error2) {
|
|
196129
|
-
log2.warn("[timeback] Failed to fetch user role, defaulting to student:", {
|
|
196130
|
-
error: error2,
|
|
196131
|
-
timebackId
|
|
196132
|
-
});
|
|
196133
|
-
return "student";
|
|
196134
|
-
}
|
|
196238
|
+
function filterEnrollmentsByGame(enrollments, gameId) {
|
|
196239
|
+
return enrollments.filter((e2) => e2.gameId === gameId).map(({ gameId: _42, ...rest }) => rest);
|
|
196135
196240
|
}
|
|
196136
|
-
|
|
196137
|
-
const
|
|
196138
|
-
|
|
196139
|
-
|
|
196241
|
+
function filterOrganizationsByEnrollments(organizations, enrollments) {
|
|
196242
|
+
const enrollmentOrgIds = new Set(enrollments.map((e2) => e2.orgId).filter(Boolean));
|
|
196243
|
+
if (enrollmentOrgIds.size === 0)
|
|
196244
|
+
return [];
|
|
196245
|
+
return organizations.filter((o42) => enrollmentOrgIds.has(o42.id));
|
|
196246
|
+
}
|
|
196247
|
+
async function fetchUserTimebackData(timebackId, gameId) {
|
|
196248
|
+
const [{ role, organizations: allOrganizations }, allEnrollments] = await Promise.all([
|
|
196249
|
+
fetchStudentFromOneRoster(timebackId),
|
|
196250
|
+
fetchEnrollmentsFromEduBridge(timebackId)
|
|
196140
196251
|
]);
|
|
196141
|
-
|
|
196252
|
+
const enrollments = gameId ? filterEnrollmentsByGame(allEnrollments, gameId) : allEnrollments;
|
|
196253
|
+
const organizations = gameId ? filterOrganizationsByEnrollments(allOrganizations, enrollments) : allOrganizations;
|
|
196254
|
+
log2.debug("[Timeback] Fetched student data", {
|
|
196255
|
+
timebackId,
|
|
196256
|
+
role,
|
|
196257
|
+
enrollments: enrollments.map((e2) => `${e2.subject}:${e2.grade}`),
|
|
196258
|
+
organizations: organizations.map((o42) => `${o42.name ?? o42.id} (${o42.type})`)
|
|
196259
|
+
});
|
|
196260
|
+
return { id: timebackId, role, enrollments, organizations };
|
|
196142
196261
|
}
|
|
196143
196262
|
var AchievementCompletionType;
|
|
196144
196263
|
((AchievementCompletionType2) => {
|
|
@@ -198546,7 +198665,7 @@ async function scanAssetDirectory(distPath) {
|
|
|
198546
198665
|
async function scanFiles(dir, baseDir = dir) {
|
|
198547
198666
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
198548
198667
|
for (const entry of entries) {
|
|
198549
|
-
const fullPath =
|
|
198668
|
+
const fullPath = join4(dir, entry.name);
|
|
198550
198669
|
if (entry.isDirectory()) {
|
|
198551
198670
|
await scanFiles(fullPath, baseDir);
|
|
198552
198671
|
} else {
|
|
@@ -198912,7 +199031,7 @@ class CloudflareProvider {
|
|
|
198912
199031
|
async function scanDirectory(dir) {
|
|
198913
199032
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
198914
199033
|
for (const entry of entries) {
|
|
198915
|
-
const fullPath =
|
|
199034
|
+
const fullPath = join5(dir, entry.name);
|
|
198916
199035
|
if (entry.isDirectory()) {
|
|
198917
199036
|
if (await scanDirectory(fullPath))
|
|
198918
199037
|
return true;
|
|
@@ -198934,7 +199053,7 @@ class CloudflareProvider {
|
|
|
198934
199053
|
async resolveAssetBasePath(dirPath) {
|
|
198935
199054
|
const entries = await readdir2(dirPath, { withFileTypes: true });
|
|
198936
199055
|
if (entries.length === 1 && entries[0]?.isDirectory()) {
|
|
198937
|
-
const unwrappedPath =
|
|
199056
|
+
const unwrappedPath = join5(dirPath, entries[0].name);
|
|
198938
199057
|
log2.debug("[CloudflareProvider] Unwrapping wrapper directory", {
|
|
198939
199058
|
wrapper: entries[0].name
|
|
198940
199059
|
});
|
|
@@ -198945,7 +199064,7 @@ class CloudflareProvider {
|
|
|
198945
199064
|
async uploadFilesToR2(dir, baseDir, bucketName) {
|
|
198946
199065
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
198947
199066
|
for (const entry of entries) {
|
|
198948
|
-
const fullPath =
|
|
199067
|
+
const fullPath = join5(dir, entry.name);
|
|
198949
199068
|
if (entry.isDirectory()) {
|
|
198950
199069
|
await this.uploadFilesToR2(fullPath, baseDir, bucketName);
|
|
198951
199070
|
} else {
|
|
@@ -199422,29 +199541,46 @@ async function seedCurrencies(db) {
|
|
|
199422
199541
|
});
|
|
199423
199542
|
}
|
|
199424
199543
|
}
|
|
199425
|
-
|
|
199426
|
-
|
|
199427
|
-
customLogger = logger3;
|
|
199544
|
+
function generateMockStudentId(userId) {
|
|
199545
|
+
return `mock-student-${userId.slice(-8)}`;
|
|
199428
199546
|
}
|
|
199429
|
-
function
|
|
199430
|
-
|
|
199431
|
-
|
|
199547
|
+
function generateTimebackId(userId, isPrimaryUser = false) {
|
|
199548
|
+
const timebackId = config.timeback.timebackId;
|
|
199549
|
+
if (!timebackId) {
|
|
199550
|
+
return null;
|
|
199432
199551
|
}
|
|
199433
|
-
|
|
199434
|
-
|
|
199435
|
-
|
|
199436
|
-
|
|
199437
|
-
};
|
|
199552
|
+
if (timebackId === "mock") {
|
|
199553
|
+
return generateMockStudentId(userId);
|
|
199554
|
+
}
|
|
199555
|
+
return isPrimaryUser ? timebackId : generateMockStudentId(userId);
|
|
199438
199556
|
}
|
|
199439
|
-
|
|
199440
|
-
|
|
199441
|
-
|
|
199442
|
-
|
|
199557
|
+
async function seedTimebackIntegrations(db, gameId, courses) {
|
|
199558
|
+
const now2 = new Date;
|
|
199559
|
+
let seededCount = 0;
|
|
199560
|
+
for (const course of courses) {
|
|
199561
|
+
const courseId = course.courseId || `mock-${course.subject.toLowerCase()}-g${course.grade}`;
|
|
199562
|
+
try {
|
|
199563
|
+
const existing = await db.query.gameTimebackIntegrations.findFirst({
|
|
199564
|
+
where: (table14, { and: and3, eq: eq3 }) => and3(eq3(table14.gameId, gameId), eq3(table14.grade, course.grade), eq3(table14.subject, course.subject))
|
|
199565
|
+
});
|
|
199566
|
+
if (existing) {
|
|
199567
|
+
continue;
|
|
199568
|
+
}
|
|
199569
|
+
await db.insert(gameTimebackIntegrations).values({
|
|
199570
|
+
gameId,
|
|
199571
|
+
courseId,
|
|
199572
|
+
grade: course.grade,
|
|
199573
|
+
subject: course.subject,
|
|
199574
|
+
totalXp: course.totalXp ?? 1000,
|
|
199575
|
+
lastVerifiedAt: now2
|
|
199576
|
+
});
|
|
199577
|
+
seededCount++;
|
|
199578
|
+
} catch (error2) {
|
|
199579
|
+
console.error(`❌ Error seeding Timeback integration for ${course.subject}:${course.grade}:`, error2);
|
|
199443
199580
|
}
|
|
199444
|
-
}
|
|
199445
|
-
|
|
199446
|
-
|
|
199447
|
-
};
|
|
199581
|
+
}
|
|
199582
|
+
return seededCount;
|
|
199583
|
+
}
|
|
199448
199584
|
async function seedCoreGames(db) {
|
|
199449
199585
|
const now2 = new Date;
|
|
199450
199586
|
const coreGames = [
|
|
@@ -199477,7 +199613,9 @@ async function seedCurrentProjectGame(db, project) {
|
|
|
199477
199613
|
where: (games3, { eq: eq3 }) => eq3(games3.slug, project.slug)
|
|
199478
199614
|
});
|
|
199479
199615
|
if (existingGame) {
|
|
199480
|
-
|
|
199616
|
+
if (project.timebackCourses && project.timebackCourses.length > 0) {
|
|
199617
|
+
await seedTimebackIntegrations(db, existingGame.id, project.timebackCourses);
|
|
199618
|
+
}
|
|
199481
199619
|
return existingGame;
|
|
199482
199620
|
}
|
|
199483
199621
|
const gameRecord = {
|
|
@@ -199497,6 +199635,12 @@ async function seedCurrentProjectGame(db, project) {
|
|
|
199497
199635
|
updatedAt: now2
|
|
199498
199636
|
};
|
|
199499
199637
|
const [newGame] = await db.insert(games).values(gameRecord).returning();
|
|
199638
|
+
if (!newGame) {
|
|
199639
|
+
throw new Error("Failed to create game record");
|
|
199640
|
+
}
|
|
199641
|
+
if (project.timebackCourses && project.timebackCourses.length > 0) {
|
|
199642
|
+
await seedTimebackIntegrations(db, newGame.id, project.timebackCourses);
|
|
199643
|
+
}
|
|
199500
199644
|
return newGame;
|
|
199501
199645
|
} catch (error2) {
|
|
199502
199646
|
console.error("❌ Error seeding project game:", error2);
|
|
@@ -199553,23 +199697,14 @@ async function seedSpriteTemplates(db) {
|
|
|
199553
199697
|
}
|
|
199554
199698
|
}
|
|
199555
199699
|
}
|
|
199556
|
-
function resolveStudentId(studentId) {
|
|
199557
|
-
if (!studentId)
|
|
199558
|
-
return null;
|
|
199559
|
-
if (studentId === "mock")
|
|
199560
|
-
return `mock-student-${crypto.randomUUID().slice(0, 8)}`;
|
|
199561
|
-
return studentId;
|
|
199562
|
-
}
|
|
199563
|
-
function getAdminTimebackId() {
|
|
199564
|
-
return resolveStudentId(config.timeback.studentId);
|
|
199565
|
-
}
|
|
199566
199700
|
async function seedDemoData(db) {
|
|
199567
199701
|
try {
|
|
199568
|
-
const
|
|
199569
|
-
for (const
|
|
199702
|
+
const primaryUserId = DEMO_USERS.player.id;
|
|
199703
|
+
for (const user of Object.values(DEMO_USERS)) {
|
|
199704
|
+
const isPrimaryUser = user.id === primaryUserId;
|
|
199570
199705
|
const userValues = {
|
|
199571
199706
|
...user,
|
|
199572
|
-
timebackId:
|
|
199707
|
+
timebackId: generateTimebackId(user.id, isPrimaryUser)
|
|
199573
199708
|
};
|
|
199574
199709
|
await db.insert(users).values(userValues).onConflictDoNothing();
|
|
199575
199710
|
}
|
|
@@ -199590,7 +199725,7 @@ async function seedDemoData(db) {
|
|
|
199590
199725
|
console.error("❌ Error seeding demo data:", error2);
|
|
199591
199726
|
throw error2;
|
|
199592
199727
|
}
|
|
199593
|
-
return DEMO_USERS.
|
|
199728
|
+
return DEMO_USERS.player;
|
|
199594
199729
|
}
|
|
199595
199730
|
async function checkIfNeedsSeeding(db) {
|
|
199596
199731
|
try {
|
|
@@ -199626,6 +199761,10 @@ async function setupServerDatabase(processedOptions, project) {
|
|
|
199626
199761
|
}
|
|
199627
199762
|
init_src();
|
|
199628
199763
|
var import_json_colorizer = __toESM2(require_dist22(), 1);
|
|
199764
|
+
var customLogger;
|
|
199765
|
+
function setLogger(logger3) {
|
|
199766
|
+
customLogger = logger3;
|
|
199767
|
+
}
|
|
199629
199768
|
function processServerOptions(port, options) {
|
|
199630
199769
|
const {
|
|
199631
199770
|
verbose = false,
|
|
@@ -199674,6 +199813,10 @@ async function startRealtimeServer(realtimeOptions, betterAuthSecret) {
|
|
|
199674
199813
|
if (!realtimeOptions.enabled) {
|
|
199675
199814
|
return null;
|
|
199676
199815
|
}
|
|
199816
|
+
if (!realtimeOptions.port) {
|
|
199817
|
+
return null;
|
|
199818
|
+
}
|
|
199819
|
+
await waitForPort(realtimeOptions.port);
|
|
199677
199820
|
if (typeof Bun === "undefined") {
|
|
199678
199821
|
try {
|
|
199679
199822
|
return Promise.resolve().then(() => (init_sandbox(), exports_sandbox)).then(({ createSandboxRealtimeServer: createSandboxRealtimeServer2 }) => createSandboxRealtimeServer2({
|
|
@@ -204929,12 +205072,32 @@ async function getUserMe(ctx) {
|
|
|
204929
205072
|
log2.error(`[API /users/me] User not found in DB for valid token ID: ${user.id}`);
|
|
204930
205073
|
throw ApiError.notFound("User not found");
|
|
204931
205074
|
}
|
|
205075
|
+
const timeback3 = userData.timebackId ? await fetchUserTimebackData(userData.timebackId, ctx.gameId) : undefined;
|
|
205076
|
+
if (ctx.gameId) {
|
|
205077
|
+
return {
|
|
205078
|
+
id: userData.id,
|
|
205079
|
+
name: userData.name,
|
|
205080
|
+
role: userData.role,
|
|
205081
|
+
username: userData.username,
|
|
205082
|
+
email: userData.email,
|
|
205083
|
+
timeback: timeback3
|
|
205084
|
+
};
|
|
205085
|
+
}
|
|
204932
205086
|
const timebackAccount = await db.query.accounts.findFirst({
|
|
204933
205087
|
where: and(eq(accounts.userId, user.id), eq(accounts.providerId, "timeback"))
|
|
204934
205088
|
});
|
|
204935
|
-
const timeback3 = userData.timebackId ? await fetchUserTimebackData(userData.timebackId) : undefined;
|
|
204936
205089
|
return {
|
|
204937
|
-
|
|
205090
|
+
id: userData.id,
|
|
205091
|
+
name: userData.name,
|
|
205092
|
+
username: userData.username,
|
|
205093
|
+
email: userData.email,
|
|
205094
|
+
emailVerified: userData.emailVerified,
|
|
205095
|
+
image: userData.image,
|
|
205096
|
+
role: userData.role,
|
|
205097
|
+
developerStatus: userData.developerStatus,
|
|
205098
|
+
characterCreated: userData.characterCreated,
|
|
205099
|
+
createdAt: userData.createdAt,
|
|
205100
|
+
updatedAt: userData.updatedAt,
|
|
204938
205101
|
hasTimebackAccount: !!timebackAccount,
|
|
204939
205102
|
timeback: timeback3
|
|
204940
205103
|
};
|
|
@@ -204945,15 +205108,104 @@ async function getUserMe(ctx) {
|
|
|
204945
205108
|
throw ApiError.internal("Internal server error", error2);
|
|
204946
205109
|
}
|
|
204947
205110
|
}
|
|
205111
|
+
init_src();
|
|
205112
|
+
function shouldMockTimeback() {
|
|
205113
|
+
return config.timeback.timebackId === "mock";
|
|
205114
|
+
}
|
|
205115
|
+
function getMockStudentProfile() {
|
|
205116
|
+
const { organization: org, role } = config.timeback;
|
|
205117
|
+
return {
|
|
205118
|
+
role: role ?? "student",
|
|
205119
|
+
organizations: [
|
|
205120
|
+
{
|
|
205121
|
+
id: org?.id ?? "PLAYCADEMY",
|
|
205122
|
+
name: org?.name ?? "Playcademy Studios",
|
|
205123
|
+
type: org?.type ?? "department",
|
|
205124
|
+
isPrimary: true
|
|
205125
|
+
}
|
|
205126
|
+
]
|
|
205127
|
+
};
|
|
205128
|
+
}
|
|
205129
|
+
async function getMockEnrollments(db) {
|
|
205130
|
+
const allIntegrations = await db.query.gameTimebackIntegrations.findMany();
|
|
205131
|
+
return allIntegrations.map((i4) => ({
|
|
205132
|
+
gameId: i4.gameId,
|
|
205133
|
+
grade: i4.grade,
|
|
205134
|
+
subject: i4.subject,
|
|
205135
|
+
courseId: i4.courseId
|
|
205136
|
+
}));
|
|
205137
|
+
}
|
|
205138
|
+
async function getMockTimebackData(db, timebackId, gameId) {
|
|
205139
|
+
const { role, organizations } = getMockStudentProfile();
|
|
205140
|
+
const allEnrollments = await getMockEnrollments(db);
|
|
205141
|
+
const enrollments = gameId ? allEnrollments.filter((e2) => e2.gameId === gameId).map(({ gameId: _5, ...rest }) => rest) : allEnrollments;
|
|
205142
|
+
log2.debug("[Timeback] Sandbox is using mock data", {
|
|
205143
|
+
timebackId,
|
|
205144
|
+
role,
|
|
205145
|
+
enrollments: enrollments.map((e2) => `${e2.subject}:${e2.grade}`),
|
|
205146
|
+
organizations: organizations.map((o5) => `${o5.name ?? o5.id}`)
|
|
205147
|
+
});
|
|
205148
|
+
return { id: timebackId, role, enrollments, organizations };
|
|
205149
|
+
}
|
|
205150
|
+
async function buildMockUserResponse(db, user, gameId) {
|
|
205151
|
+
const timeback3 = user.timebackId ? await getMockTimebackData(db, user.timebackId, gameId) : undefined;
|
|
205152
|
+
if (gameId) {
|
|
205153
|
+
return {
|
|
205154
|
+
id: user.id,
|
|
205155
|
+
name: user.name,
|
|
205156
|
+
role: user.role,
|
|
205157
|
+
username: user.username,
|
|
205158
|
+
email: user.email,
|
|
205159
|
+
timeback: timeback3
|
|
205160
|
+
};
|
|
205161
|
+
}
|
|
205162
|
+
const timebackAccount = await db.query.accounts.findFirst({
|
|
205163
|
+
where: and(eq(accounts.userId, user.id), eq(accounts.providerId, "timeback"))
|
|
205164
|
+
});
|
|
205165
|
+
return {
|
|
205166
|
+
id: user.id,
|
|
205167
|
+
name: user.name,
|
|
205168
|
+
username: user.username,
|
|
205169
|
+
email: user.email,
|
|
205170
|
+
emailVerified: user.emailVerified,
|
|
205171
|
+
image: user.image,
|
|
205172
|
+
role: user.role,
|
|
205173
|
+
developerStatus: user.developerStatus,
|
|
205174
|
+
characterCreated: user.characterCreated,
|
|
205175
|
+
createdAt: user.createdAt,
|
|
205176
|
+
updatedAt: user.updatedAt,
|
|
205177
|
+
hasTimebackAccount: !!timebackAccount,
|
|
205178
|
+
timeback: timeback3
|
|
205179
|
+
};
|
|
205180
|
+
}
|
|
204948
205181
|
var usersRouter = new Hono2;
|
|
204949
205182
|
usersRouter.get("/me", async (c3) => {
|
|
204950
|
-
const
|
|
204951
|
-
|
|
204952
|
-
|
|
204953
|
-
|
|
204954
|
-
|
|
204955
|
-
}
|
|
205183
|
+
const user = c3.get("user");
|
|
205184
|
+
const gameId = c3.get("gameId");
|
|
205185
|
+
if (!user) {
|
|
205186
|
+
const error2 = ApiError.unauthorized("Valid session or bearer token required");
|
|
205187
|
+
return c3.json(createErrorResponse(error2), error2.statusCode);
|
|
205188
|
+
}
|
|
204956
205189
|
try {
|
|
205190
|
+
if (shouldMockTimeback()) {
|
|
205191
|
+
const db = c3.get("db");
|
|
205192
|
+
const userData2 = await db.query.users.findFirst({
|
|
205193
|
+
where: eq(users.id, user.id)
|
|
205194
|
+
});
|
|
205195
|
+
if (!userData2) {
|
|
205196
|
+
const error2 = ApiError.notFound("User not found");
|
|
205197
|
+
return c3.json(createErrorResponse(error2), error2.statusCode);
|
|
205198
|
+
}
|
|
205199
|
+
const response = await buildMockUserResponse(db, userData2, gameId);
|
|
205200
|
+
return c3.json(response);
|
|
205201
|
+
}
|
|
205202
|
+
const ctx = {
|
|
205203
|
+
user,
|
|
205204
|
+
params: {},
|
|
205205
|
+
url: new URL(c3.req.url),
|
|
205206
|
+
request: c3.req.raw,
|
|
205207
|
+
gameId
|
|
205208
|
+
};
|
|
204957
205209
|
const userData = await getUserMe(ctx);
|
|
204958
205210
|
return c3.json(userData);
|
|
204959
205211
|
} catch (error2) {
|
|
@@ -210203,7 +210455,7 @@ async function getTodayTimeBackXp(ctx) {
|
|
|
210203
210455
|
throw error2;
|
|
210204
210456
|
if (error2 instanceof InvalidTimezoneError)
|
|
210205
210457
|
throw ApiError.badRequest(error2.message);
|
|
210206
|
-
log2.error("[
|
|
210458
|
+
log2.error("[Timeback] getTodayTimeBackXp failed", { error: error2 });
|
|
210207
210459
|
throw ApiError.internal("Failed to get today's TimeBack XP", error2);
|
|
210208
210460
|
}
|
|
210209
210461
|
}
|
|
@@ -210219,7 +210471,7 @@ async function getTotalTimeBackXp(ctx) {
|
|
|
210219
210471
|
totalXp: Number(result[0]?.totalXp) || 0
|
|
210220
210472
|
};
|
|
210221
210473
|
} catch (error2) {
|
|
210222
|
-
log2.error("[
|
|
210474
|
+
log2.error("[Timeback] getTotalTimeBackXp failed", { error: error2 });
|
|
210223
210475
|
throw ApiError.internal("Failed to get total TimeBack XP", error2);
|
|
210224
210476
|
}
|
|
210225
210477
|
}
|
|
@@ -210269,7 +210521,7 @@ async function updateTodayTimeBackXp(ctx) {
|
|
|
210269
210521
|
} catch (error2) {
|
|
210270
210522
|
if (error2 instanceof ApiError)
|
|
210271
210523
|
throw error2;
|
|
210272
|
-
log2.error("[
|
|
210524
|
+
log2.error("[Timeback] updateTodayTimeBackXp failed", { error: error2 });
|
|
210273
210525
|
throw ApiError.internal("Failed to update today's TimeBack XP", error2);
|
|
210274
210526
|
}
|
|
210275
210527
|
}
|
|
@@ -210304,7 +210556,7 @@ async function getTimeBackXpHistory(ctx) {
|
|
|
210304
210556
|
}))
|
|
210305
210557
|
};
|
|
210306
210558
|
} catch (error2) {
|
|
210307
|
-
log2.error("[
|
|
210559
|
+
log2.error("[Timeback] getTimeBackXpHistory failed", { error: error2 });
|
|
210308
210560
|
throw ApiError.internal("Failed to get TimeBack XP history", error2);
|
|
210309
210561
|
}
|
|
210310
210562
|
}
|
|
@@ -210319,7 +210571,7 @@ async function getStudentEnrollments(ctx) {
|
|
|
210319
210571
|
throw ApiError.badRequest("Missing timebackId parameter");
|
|
210320
210572
|
}
|
|
210321
210573
|
log2.debug("[API] Getting student enrollments", { userId: user.id, timebackId });
|
|
210322
|
-
const enrollments = await
|
|
210574
|
+
const enrollments = await fetchEnrollmentsFromEduBridge(timebackId);
|
|
210323
210575
|
log2.info("[API] Retrieved student enrollments", {
|
|
210324
210576
|
userId: user.id,
|
|
210325
210577
|
timebackId,
|
|
@@ -210519,13 +210771,23 @@ timebackRouter.post("/end-activity", async (c3) => {
|
|
|
210519
210771
|
});
|
|
210520
210772
|
timebackRouter.get("/enrollments/:timebackId", async (c3) => {
|
|
210521
210773
|
const timebackId = c3.req.param("timebackId");
|
|
210522
|
-
const
|
|
210523
|
-
|
|
210524
|
-
|
|
210525
|
-
|
|
210526
|
-
|
|
210527
|
-
};
|
|
210774
|
+
const user = c3.get("user");
|
|
210775
|
+
if (!user) {
|
|
210776
|
+
const error2 = ApiError.unauthorized("Must be logged in to get enrollments");
|
|
210777
|
+
return c3.json(createErrorResponse(error2), error2.statusCode);
|
|
210778
|
+
}
|
|
210528
210779
|
try {
|
|
210780
|
+
if (shouldMockTimeback()) {
|
|
210781
|
+
const db = c3.get("db");
|
|
210782
|
+
const enrollments2 = await getMockEnrollments(db);
|
|
210783
|
+
return c3.json({ enrollments: enrollments2 });
|
|
210784
|
+
}
|
|
210785
|
+
const ctx = {
|
|
210786
|
+
user,
|
|
210787
|
+
params: { timebackId },
|
|
210788
|
+
url: new URL(c3.req.url),
|
|
210789
|
+
request: c3.req.raw
|
|
210790
|
+
};
|
|
210529
210791
|
const result = await getStudentEnrollments(ctx);
|
|
210530
210792
|
return c3.json(result);
|
|
210531
210793
|
} catch (error2) {
|
|
@@ -210833,6 +211095,7 @@ function registerRoutes(app) {
|
|
|
210833
211095
|
}
|
|
210834
211096
|
var version3 = package_default.version;
|
|
210835
211097
|
async function startServer(port, project, options = {}) {
|
|
211098
|
+
await waitForPort(port);
|
|
210836
211099
|
const processedOptions = processServerOptions(port, options);
|
|
210837
211100
|
const db = await setupServerDatabase(processedOptions, project);
|
|
210838
211101
|
const app = createApp(db, {
|
|
@@ -210845,50 +211108,75 @@ async function startServer(port, project, options = {}) {
|
|
|
210845
211108
|
return {
|
|
210846
211109
|
main: mainServer,
|
|
210847
211110
|
realtime: realtimeServer,
|
|
211111
|
+
timebackMode: getTimebackDisplayMode(),
|
|
211112
|
+
setRole: (role) => {
|
|
211113
|
+
config.timeback.role = role;
|
|
211114
|
+
},
|
|
210848
211115
|
stop: () => {
|
|
210849
|
-
|
|
210850
|
-
|
|
210851
|
-
|
|
210852
|
-
|
|
211116
|
+
return new Promise((resolve2) => {
|
|
211117
|
+
if (realtimeServer?.stop)
|
|
211118
|
+
realtimeServer.stop();
|
|
211119
|
+
if (mainServer.close) {
|
|
211120
|
+
mainServer.close(() => resolve2());
|
|
211121
|
+
} else {
|
|
211122
|
+
resolve2();
|
|
211123
|
+
}
|
|
211124
|
+
});
|
|
210853
211125
|
}
|
|
210854
211126
|
};
|
|
210855
211127
|
}
|
|
210856
211128
|
|
|
210857
211129
|
// src/lib/logging/adapter.ts
|
|
210858
|
-
|
|
210859
|
-
function formatTimestamp2() {
|
|
210860
|
-
const now2 = new Date;
|
|
210861
|
-
const hours = now2.getHours();
|
|
210862
|
-
const minutes = now2.getMinutes().toString().padStart(2, "0");
|
|
210863
|
-
const seconds = now2.getSeconds().toString().padStart(2, "0");
|
|
210864
|
-
const ampm = hours >= 12 ? "PM" : "AM";
|
|
210865
|
-
const displayHours = hours % 12 || 12;
|
|
210866
|
-
return import_picocolors3.dim(`${displayHours}:${minutes}:${seconds} ${ampm}`);
|
|
210867
|
-
}
|
|
210868
|
-
function createLoggerAdapter(prefix2) {
|
|
210869
|
-
const formattedPrefix = import_picocolors3.dim(`(${prefix2})`);
|
|
210870
|
-
const label = import_picocolors3.cyan(import_picocolors3.bold("[playcademy]"));
|
|
211130
|
+
function createLoggerAdapter(domain) {
|
|
210871
211131
|
return {
|
|
210872
|
-
info: (msg) => console.log(`${
|
|
210873
|
-
warn: (msg) => console.warn(`${
|
|
210874
|
-
error: (msg) => console.error(`${
|
|
211132
|
+
info: (msg) => console.log(`${createLogPrefix("playcademy", domain)} ${msg}`),
|
|
211133
|
+
warn: (msg) => console.warn(`${createLogPrefix("playcademy", domain)} ${msg}`),
|
|
211134
|
+
error: (msg) => console.error(`${createLogPrefix("playcademy", domain)} ${msg}`)
|
|
210875
211135
|
};
|
|
210876
211136
|
}
|
|
210877
211137
|
// src/lib/logging/utils.ts
|
|
210878
|
-
var
|
|
210879
|
-
|
|
211138
|
+
var import_picocolors3 = __toESM(require_picocolors(), 1);
|
|
211139
|
+
|
|
211140
|
+
// ../utils/src/string.ts
|
|
211141
|
+
function pluralize(count2, singular, plural) {
|
|
211142
|
+
return count2 === 1 ? singular : plural || `${singular}s`;
|
|
211143
|
+
}
|
|
211144
|
+
|
|
211145
|
+
// src/lib/logging/utils.ts
|
|
211146
|
+
function createBackendBannerOptions(backendPort, vitePort) {
|
|
211147
|
+
if (!backendPort)
|
|
211148
|
+
return;
|
|
211149
|
+
return { port: backendPort, vitePort };
|
|
211150
|
+
}
|
|
211151
|
+
function createTimebackBannerOptions(timebackMode, courseCount) {
|
|
211152
|
+
if (!timebackMode || !courseCount)
|
|
211153
|
+
return;
|
|
211154
|
+
return { courseCount, mode: timebackMode };
|
|
211155
|
+
}
|
|
211156
|
+
function printBanner(viteConfig, options) {
|
|
210880
211157
|
const INDENT = " ".repeat(2);
|
|
211158
|
+
const { version: version4, gameName, sandbox, backend, realtimePort, timeback } = options;
|
|
210881
211159
|
viteConfig.logger.info("");
|
|
210882
|
-
viteConfig.logger.info(`${INDENT}${
|
|
211160
|
+
viteConfig.logger.info(`${INDENT}${import_picocolors3.green(import_picocolors3.bold("PLAYCADEMY"))} ${import_picocolors3.green(`v${version4}`)}`);
|
|
210883
211161
|
viteConfig.logger.info("");
|
|
210884
|
-
|
|
210885
|
-
|
|
210886
|
-
|
|
210887
|
-
|
|
210888
|
-
viteConfig.logger.info(`${INDENT}${
|
|
211162
|
+
if (gameName) {
|
|
211163
|
+
viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Game:")} ${import_picocolors3.cyan(gameName)}`);
|
|
211164
|
+
}
|
|
211165
|
+
if (sandbox?.enabled) {
|
|
211166
|
+
viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Sandbox:")} ${import_picocolors3.cyan(`http://localhost:${import_picocolors3.bold(sandbox.port.toString())}/api`)}`);
|
|
211167
|
+
} else if (sandbox) {
|
|
211168
|
+
viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Sandbox:")} ${import_picocolors3.cyan("Disabled")}`);
|
|
211169
|
+
}
|
|
211170
|
+
if (backend) {
|
|
211171
|
+
const backendUrl = backend.vitePort ? `http://localhost:${import_picocolors3.bold(backend.vitePort.toString())}/api ${import_picocolors3.dim(`(via ${backend.port})`)}` : `http://localhost:${import_picocolors3.bold(backend.port.toString())}/api`;
|
|
211172
|
+
viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Backend:")} ${import_picocolors3.cyan(backendUrl)}`);
|
|
210889
211173
|
}
|
|
210890
|
-
if (
|
|
210891
|
-
viteConfig.logger.info(`${INDENT}${
|
|
211174
|
+
if (realtimePort) {
|
|
211175
|
+
viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Realtime:")} ${import_picocolors3.cyan(`ws://localhost:${import_picocolors3.bold(realtimePort.toString())}`)}`);
|
|
211176
|
+
}
|
|
211177
|
+
if (timeback && timeback.courseCount > 0) {
|
|
211178
|
+
const label = `${timeback.courseCount} ${pluralize(timeback.courseCount, "course")} ${import_picocolors3.dim(`(${timeback.mode})`)}`;
|
|
211179
|
+
viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Timeback:")} ${import_picocolors3.cyan(label)}`);
|
|
210892
211180
|
}
|
|
210893
211181
|
viteConfig.logger.info("");
|
|
210894
211182
|
}
|
|
@@ -210896,13 +211184,29 @@ function printBanner(viteConfig, servers, projectInfo, pluginVersion) {
|
|
|
210896
211184
|
import fs6 from "node:fs";
|
|
210897
211185
|
import path5 from "node:path";
|
|
210898
211186
|
import { loadPlaycademyConfig } from "playcademy/utils";
|
|
210899
|
-
|
|
211187
|
+
function extractTimebackCourses(config2, timebackOptions) {
|
|
211188
|
+
const courses = config2?.integrations?.timeback?.courses;
|
|
211189
|
+
if (!courses || courses.length === 0)
|
|
211190
|
+
return;
|
|
211191
|
+
return courses.map((course) => {
|
|
211192
|
+
const key = `${course.subject}:${course.grade}`;
|
|
211193
|
+
const overrideId = timebackOptions?.courses?.[key];
|
|
211194
|
+
const courseId = overrideId && overrideId !== "mock" ? overrideId : undefined;
|
|
211195
|
+
return {
|
|
211196
|
+
subject: course.subject,
|
|
211197
|
+
grade: course.grade,
|
|
211198
|
+
courseId,
|
|
211199
|
+
totalXp: course.totalXp,
|
|
211200
|
+
masterableUnits: course.masterableUnits
|
|
211201
|
+
};
|
|
211202
|
+
});
|
|
211203
|
+
}
|
|
211204
|
+
async function extractProjectInfo(viteConfig, timebackOptions) {
|
|
210900
211205
|
const projectRoot = viteConfig.root;
|
|
210901
211206
|
const directoryName = path5.basename(projectRoot);
|
|
210902
|
-
let
|
|
211207
|
+
let config2 = null;
|
|
210903
211208
|
try {
|
|
210904
|
-
|
|
210905
|
-
configName = config2?.name;
|
|
211209
|
+
config2 = await loadPlaycademyConfig();
|
|
210906
211210
|
} catch {}
|
|
210907
211211
|
let packageJson = {};
|
|
210908
211212
|
try {
|
|
@@ -210912,7 +211216,7 @@ async function extractProjectInfo(viteConfig) {
|
|
|
210912
211216
|
packageJson = JSON.parse(packageJsonContent);
|
|
210913
211217
|
}
|
|
210914
211218
|
} catch {}
|
|
210915
|
-
const name3 =
|
|
211219
|
+
const name3 = config2?.name || packageJson.name || "";
|
|
210916
211220
|
let slug = name3;
|
|
210917
211221
|
if (slug.includes("/")) {
|
|
210918
211222
|
slug = slug.split("/")[1] || slug;
|
|
@@ -210924,20 +211228,42 @@ async function extractProjectInfo(viteConfig) {
|
|
|
210924
211228
|
return {
|
|
210925
211229
|
slug,
|
|
210926
211230
|
displayName,
|
|
210927
|
-
version:
|
|
210928
|
-
description:
|
|
211231
|
+
version: packageJson.version || "dev",
|
|
211232
|
+
description: packageJson.description,
|
|
211233
|
+
timebackCourses: extractTimebackCourses(config2, timebackOptions)
|
|
210929
211234
|
};
|
|
210930
211235
|
}
|
|
210931
211236
|
|
|
210932
211237
|
// src/lib/sandbox/timeback.ts
|
|
210933
|
-
var
|
|
210934
|
-
|
|
210935
|
-
|
|
210936
|
-
|
|
210937
|
-
|
|
210938
|
-
|
|
210939
|
-
|
|
210940
|
-
|
|
211238
|
+
var import_picocolors4 = __toESM(require_picocolors(), 1);
|
|
211239
|
+
// ../constants/src/overworld.ts
|
|
211240
|
+
var ITEM_SLUGS4 = {
|
|
211241
|
+
PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
|
|
211242
|
+
PLAYCADEMY_XP: "PLAYCADEMY_XP",
|
|
211243
|
+
FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
|
|
211244
|
+
EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
|
|
211245
|
+
FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
|
|
211246
|
+
COMMON_SWORD: "COMMON_SWORD",
|
|
211247
|
+
SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
|
|
211248
|
+
SMALL_BACKPACK: "SMALL_BACKPACK",
|
|
211249
|
+
LAVA_LAMP: "LAVA_LAMP",
|
|
211250
|
+
BOOMBOX: "BOOMBOX",
|
|
211251
|
+
CABIN_BED: "CABIN_BED"
|
|
211252
|
+
};
|
|
211253
|
+
var CURRENCIES4 = {
|
|
211254
|
+
PRIMARY: ITEM_SLUGS4.PLAYCADEMY_CREDITS,
|
|
211255
|
+
XP: ITEM_SLUGS4.PLAYCADEMY_XP
|
|
211256
|
+
};
|
|
211257
|
+
var BADGES4 = {
|
|
211258
|
+
FOUNDING_MEMBER: ITEM_SLUGS4.FOUNDING_MEMBER_BADGE,
|
|
211259
|
+
EARLY_ADOPTER: ITEM_SLUGS4.EARLY_ADOPTER_BADGE,
|
|
211260
|
+
FIRST_GAME: ITEM_SLUGS4.FIRST_GAME_BADGE
|
|
211261
|
+
};
|
|
211262
|
+
// ../constants/src/timeback.ts
|
|
211263
|
+
var TIMEBACK_ORG_SOURCED_ID3 = "PLAYCADEMY";
|
|
211264
|
+
var TIMEBACK_ORG_NAME3 = "Playcademy Studios";
|
|
211265
|
+
var TIMEBACK_ORG_TYPE3 = "department";
|
|
211266
|
+
// src/lib/sandbox/timeback.ts
|
|
210941
211267
|
function detectTimebackOptions() {
|
|
210942
211268
|
if (process.env.TIMEBACK_LOCAL === "true") {
|
|
210943
211269
|
return {
|
|
@@ -210945,7 +211271,7 @@ function detectTimebackOptions() {
|
|
|
210945
211271
|
onerosterApiUrl: process.env.TIMEBACK_ONEROSTER_API_URL,
|
|
210946
211272
|
caliperApiUrl: process.env.TIMEBACK_CALIPER_API_URL,
|
|
210947
211273
|
courseId: process.env.SANDBOX_TIMEBACK_COURSE_ID,
|
|
210948
|
-
|
|
211274
|
+
timebackId: process.env.SANDBOX_TIMEBACK_STUDENT_ID
|
|
210949
211275
|
};
|
|
210950
211276
|
}
|
|
210951
211277
|
if (process.env.TIMEBACK_API_CLIENT_ID) {
|
|
@@ -210957,7 +211283,7 @@ function detectTimebackOptions() {
|
|
|
210957
211283
|
clientSecret: process.env.TIMEBACK_API_CLIENT_SECRET,
|
|
210958
211284
|
authUrl: process.env.TIMEBACK_API_AUTH_URL,
|
|
210959
211285
|
courseId: process.env.SANDBOX_TIMEBACK_COURSE_ID,
|
|
210960
|
-
|
|
211286
|
+
timebackId: process.env.SANDBOX_TIMEBACK_STUDENT_ID
|
|
210961
211287
|
};
|
|
210962
211288
|
}
|
|
210963
211289
|
return;
|
|
@@ -210971,59 +211297,77 @@ function hasTimebackCredentials2() {
|
|
|
210971
211297
|
}
|
|
210972
211298
|
return !!(config2.onerosterApiUrl && config2.clientId && config2.clientSecret && config2.authUrl);
|
|
210973
211299
|
}
|
|
210974
|
-
function validateTimebackConfig(viteConfig,
|
|
210975
|
-
if (!
|
|
211300
|
+
function validateTimebackConfig(viteConfig, timeback2) {
|
|
211301
|
+
if (!timeback2?.courses)
|
|
210976
211302
|
return;
|
|
210977
|
-
const realCourses = Object.entries(
|
|
211303
|
+
const realCourses = Object.entries(timeback2.courses).filter(([, value]) => value !== "mock");
|
|
210978
211304
|
if (realCourses.length > 0 && !hasTimebackCredentials2()) {
|
|
210979
211305
|
const courseList = realCourses.map(([key]) => key).join(", ");
|
|
210980
211306
|
viteConfig.logger.warn("");
|
|
210981
|
-
viteConfig.logger.warn(
|
|
210982
|
-
viteConfig.logger.warn(
|
|
210983
|
-
viteConfig.logger.warn(
|
|
211307
|
+
viteConfig.logger.warn(import_picocolors4.default.yellow(`⚠️ TimeBack: Real course IDs for ${import_picocolors4.default.bold(courseList)} but credentials missing.`));
|
|
211308
|
+
viteConfig.logger.warn(import_picocolors4.default.dim(` Required: TIMEBACK_API_CLIENT_ID, TIMEBACK_API_CLIENT_SECRET, TIMEBACK_API_AUTH_URL, TIMEBACK_ONEROSTER_API_URL`));
|
|
211309
|
+
viteConfig.logger.warn(import_picocolors4.default.dim(` Or use 'mock' for local testing.`));
|
|
210984
211310
|
viteConfig.logger.warn("");
|
|
210985
211311
|
}
|
|
210986
211312
|
}
|
|
210987
|
-
function
|
|
210988
|
-
if (!
|
|
210989
|
-
return
|
|
211313
|
+
function resolveOrganization(orgConfig) {
|
|
211314
|
+
if (!orgConfig || orgConfig === "mock") {
|
|
211315
|
+
return {
|
|
211316
|
+
id: TIMEBACK_ORG_SOURCED_ID3,
|
|
211317
|
+
name: TIMEBACK_ORG_NAME3,
|
|
211318
|
+
type: TIMEBACK_ORG_TYPE3
|
|
211319
|
+
};
|
|
211320
|
+
}
|
|
211321
|
+
return {
|
|
211322
|
+
id: orgConfig.id ?? TIMEBACK_ORG_SOURCED_ID3,
|
|
211323
|
+
name: orgConfig.name ?? TIMEBACK_ORG_NAME3,
|
|
211324
|
+
type: orgConfig.type ?? TIMEBACK_ORG_TYPE3
|
|
211325
|
+
};
|
|
211326
|
+
}
|
|
211327
|
+
function resolveOrganizations(orgConfig) {
|
|
211328
|
+
const org = resolveOrganization(orgConfig);
|
|
211329
|
+
return [
|
|
211330
|
+
{
|
|
211331
|
+
id: org.id,
|
|
211332
|
+
name: org.name ?? null,
|
|
211333
|
+
type: org.type ?? TIMEBACK_ORG_TYPE3,
|
|
211334
|
+
isPrimary: true
|
|
211335
|
+
}
|
|
211336
|
+
];
|
|
211337
|
+
}
|
|
211338
|
+
function generateTimebackJson(context) {
|
|
211339
|
+
if (!context?.baseCourses || context.baseCourses.length === 0) {
|
|
211340
|
+
return "null";
|
|
210990
211341
|
}
|
|
211342
|
+
const { baseCourses, overrides } = context;
|
|
210991
211343
|
const enrollments = [];
|
|
210992
|
-
for (const
|
|
210993
|
-
const
|
|
210994
|
-
const
|
|
210995
|
-
|
|
210996
|
-
const grade = gradeStr ? parseInt(gradeStr, 10) : NaN;
|
|
210997
|
-
if (!subject || isNaN(grade))
|
|
211344
|
+
for (const course of baseCourses) {
|
|
211345
|
+
const key = `${course.subject}:${course.grade}`;
|
|
211346
|
+
const override = overrides?.courses?.[key];
|
|
211347
|
+
if (override === null)
|
|
210998
211348
|
continue;
|
|
210999
|
-
const courseId =
|
|
211000
|
-
enrollments.push({
|
|
211349
|
+
const courseId = override && override !== "mock" ? override : `mock-${course.subject.toLowerCase()}-g${course.grade}`;
|
|
211350
|
+
enrollments.push({
|
|
211351
|
+
subject: course.subject,
|
|
211352
|
+
grade: course.grade,
|
|
211353
|
+
courseId
|
|
211354
|
+
});
|
|
211001
211355
|
}
|
|
211002
211356
|
if (enrollments.length === 0) {
|
|
211003
|
-
return;
|
|
211357
|
+
return "null";
|
|
211004
211358
|
}
|
|
211005
211359
|
const roleOverride = getTimebackRoleOverride();
|
|
211006
|
-
const role = roleOverride ??
|
|
211007
|
-
|
|
211360
|
+
const role = roleOverride ?? overrides?.role ?? "student";
|
|
211361
|
+
const organizations = resolveOrganizations(overrides?.organization);
|
|
211362
|
+
return JSON.stringify({ role, enrollments, organizations });
|
|
211008
211363
|
}
|
|
211009
211364
|
|
|
211010
211365
|
// src/lib/sandbox/server.ts
|
|
211011
|
-
function printSandboxInfo(viteConfig, apiPort, realtimePort, projectInfo, realtimeEnabled) {
|
|
211012
|
-
viteConfig.logger.info("");
|
|
211013
|
-
viteConfig.logger.info(` ${import_picocolors6.default.green(import_picocolors6.default.bold("PLAYCADEMY"))} ${import_picocolors6.default.green(`v${version3}`)}`);
|
|
211014
|
-
viteConfig.logger.info("");
|
|
211015
|
-
viteConfig.logger.info(` ${import_picocolors6.default.green("➜")} ${import_picocolors6.default.bold("Game:")} ${import_picocolors6.default.cyan(projectInfo.slug)}`);
|
|
211016
|
-
viteConfig.logger.info(` ${import_picocolors6.default.green("➜")} ${import_picocolors6.default.bold("Sandbox:")} ${import_picocolors6.default.cyan(`http://localhost:${import_picocolors6.default.bold(apiPort.toString())}/api`)}`);
|
|
211017
|
-
if (realtimeEnabled) {
|
|
211018
|
-
viteConfig.logger.info(` ${import_picocolors6.default.green("➜")} ${import_picocolors6.default.bold("Realtime:")} ${import_picocolors6.default.cyan(`ws://localhost:${import_picocolors6.default.bold(realtimePort.toString())}`)}`);
|
|
211019
|
-
}
|
|
211020
|
-
viteConfig.logger.info("");
|
|
211021
|
-
}
|
|
211022
211366
|
async function startSandbox(viteConfig, autoStart = true, options = {}) {
|
|
211023
211367
|
const {
|
|
211368
|
+
port = DEFAULT_PORTS2.SANDBOX,
|
|
211024
211369
|
verbose = false,
|
|
211025
211370
|
customUrl,
|
|
211026
|
-
quiet = false,
|
|
211027
211371
|
recreateDb = false,
|
|
211028
211372
|
seed = true,
|
|
211029
211373
|
memoryOnly = false,
|
|
@@ -211031,16 +211375,17 @@ async function startSandbox(viteConfig, autoStart = true, options = {}) {
|
|
|
211031
211375
|
realtimeEnabled = false,
|
|
211032
211376
|
realtimePort,
|
|
211033
211377
|
logLevel = "info",
|
|
211034
|
-
|
|
211378
|
+
timebackOptions
|
|
211035
211379
|
} = options;
|
|
211380
|
+
const timebackDisabled = timebackOptions === false;
|
|
211036
211381
|
if (!autoStart || viteConfig.command !== "serve") {
|
|
211037
211382
|
const baseUrl = customUrl ?? `http://localhost:${DEFAULT_PORTS2.SANDBOX}`;
|
|
211038
211383
|
const deriveRealtimeUrl = (url) => {
|
|
211039
211384
|
try {
|
|
211040
211385
|
const u4 = new URL(url);
|
|
211041
|
-
const
|
|
211386
|
+
const port2 = Number(u4.port || (u4.protocol === "https:" ? 443 : 80));
|
|
211042
211387
|
const wsProto = u4.protocol === "https:" ? "wss:" : "ws:";
|
|
211043
|
-
return `${wsProto}//${u4.hostname}:${
|
|
211388
|
+
return `${wsProto}//${u4.hostname}:${port2 + 1}`;
|
|
211044
211389
|
} catch {
|
|
211045
211390
|
return "ws://localhost:4322";
|
|
211046
211391
|
}
|
|
@@ -211055,57 +211400,49 @@ async function startSandbox(viteConfig, autoStart = true, options = {}) {
|
|
|
211055
211400
|
};
|
|
211056
211401
|
}
|
|
211057
211402
|
try {
|
|
211058
|
-
const
|
|
211059
|
-
const
|
|
211060
|
-
const projectInfo = await extractProjectInfo(viteConfig);
|
|
211061
|
-
let
|
|
211062
|
-
|
|
211063
|
-
|
|
211064
|
-
|
|
211065
|
-
|
|
211066
|
-
|
|
211067
|
-
|
|
211068
|
-
|
|
211403
|
+
const baseUrl = `http://localhost:${port}`;
|
|
211404
|
+
const effectiveTimebackOptions = timebackDisabled ? undefined : timebackOptions || undefined;
|
|
211405
|
+
const projectInfo = await extractProjectInfo(viteConfig, effectiveTimebackOptions);
|
|
211406
|
+
let sandboxTimebackOptions = detectTimebackOptions();
|
|
211407
|
+
const effectiveTimebackId = timebackDisabled ? undefined : effectiveTimebackOptions?.id ?? sandboxTimebackOptions?.timebackId ?? (projectInfo.timebackCourses?.length ? "mock" : undefined);
|
|
211408
|
+
if (effectiveTimebackId) {
|
|
211409
|
+
sandboxTimebackOptions = {
|
|
211410
|
+
...sandboxTimebackOptions,
|
|
211411
|
+
timebackId: effectiveTimebackId,
|
|
211412
|
+
organization: resolveOrganization(effectiveTimebackOptions?.organization),
|
|
211413
|
+
role: effectiveTimebackOptions?.role
|
|
211414
|
+
};
|
|
211415
|
+
}
|
|
211416
|
+
const finalRealtimePort = realtimePort ?? port + 1;
|
|
211069
211417
|
const realtimeUrl = `ws://localhost:${finalRealtimePort}`;
|
|
211070
|
-
const server = await startServer(
|
|
211418
|
+
const server = await startServer(port, projectInfo, {
|
|
211071
211419
|
verbose,
|
|
211072
|
-
quiet,
|
|
211420
|
+
quiet: true,
|
|
211073
211421
|
seed,
|
|
211074
211422
|
memoryOnly,
|
|
211075
211423
|
databasePath,
|
|
211076
211424
|
recreateDb,
|
|
211077
211425
|
logLevel,
|
|
211078
211426
|
realtime: { enabled: realtimeEnabled, port: finalRealtimePort },
|
|
211079
|
-
timeback:
|
|
211427
|
+
timeback: sandboxTimebackOptions,
|
|
211080
211428
|
logger: createLoggerAdapter("sandbox")
|
|
211081
211429
|
});
|
|
211082
|
-
writeServerInfo("sandbox", {
|
|
211083
|
-
pid: process.pid,
|
|
211084
|
-
port: sandboxPort,
|
|
211085
|
-
url: baseUrl,
|
|
211086
|
-
startedAt: Date.now(),
|
|
211087
|
-
projectRoot: viteConfig.root
|
|
211088
|
-
});
|
|
211089
|
-
if (!quiet) {
|
|
211090
|
-
setTimeout(() => {
|
|
211091
|
-
printSandboxInfo(viteConfig, sandboxPort, finalRealtimePort, projectInfo, realtimeEnabled);
|
|
211092
|
-
}, 100);
|
|
211093
|
-
}
|
|
211094
211430
|
return {
|
|
211095
211431
|
baseUrl,
|
|
211096
211432
|
realtimeUrl,
|
|
211097
|
-
port
|
|
211433
|
+
port,
|
|
211098
211434
|
realtimePort: realtimeEnabled ? finalRealtimePort : undefined,
|
|
211099
211435
|
project: projectInfo,
|
|
211100
|
-
|
|
211101
|
-
|
|
211436
|
+
timebackMode: server.timebackMode,
|
|
211437
|
+
setRole: server.setRole,
|
|
211438
|
+
cleanup: async () => {
|
|
211102
211439
|
if (server && typeof server.stop === "function") {
|
|
211103
|
-
server.stop();
|
|
211440
|
+
await server.stop();
|
|
211104
211441
|
}
|
|
211105
211442
|
}
|
|
211106
211443
|
};
|
|
211107
211444
|
} catch (error2) {
|
|
211108
|
-
viteConfig.logger.error(
|
|
211445
|
+
viteConfig.logger.error(import_picocolors5.default.red(`[Playcademy] Failed to start sandbox: ${error2}`));
|
|
211109
211446
|
return {
|
|
211110
211447
|
baseUrl: `http://localhost:${DEFAULT_PORTS2.SANDBOX}`,
|
|
211111
211448
|
realtimeUrl: "ws://localhost:4322",
|
|
@@ -211116,216 +211453,212 @@ async function startSandbox(viteConfig, autoStart = true, options = {}) {
|
|
|
211116
211453
|
};
|
|
211117
211454
|
}
|
|
211118
211455
|
}
|
|
211119
|
-
//
|
|
211120
|
-
var
|
|
211121
|
-
|
|
211122
|
-
|
|
211123
|
-
|
|
211124
|
-
|
|
211125
|
-
|
|
211126
|
-
|
|
211127
|
-
|
|
211128
|
-
|
|
211129
|
-
|
|
211130
|
-
|
|
211131
|
-
|
|
211132
|
-
|
|
211133
|
-
|
|
211134
|
-
|
|
211135
|
-
|
|
211136
|
-
|
|
211137
|
-
|
|
211138
|
-
|
|
211139
|
-
|
|
211140
|
-
|
|
211141
|
-
|
|
211142
|
-
|
|
211143
|
-
|
|
211144
|
-
|
|
211145
|
-
|
|
211146
|
-
|
|
211147
|
-
|
|
211148
|
-
|
|
211149
|
-
|
|
211150
|
-
|
|
211151
|
-
|
|
211152
|
-
|
|
211153
|
-
|
|
211154
|
-
|
|
211155
|
-
|
|
211156
|
-
|
|
211157
|
-
|
|
211158
|
-
|
|
211159
|
-
|
|
211160
|
-
|
|
211161
|
-
|
|
211162
|
-
|
|
211163
|
-
|
|
211164
|
-
|
|
211165
|
-
|
|
211166
|
-
|
|
211167
|
-
|
|
211168
|
-
|
|
211169
|
-
|
|
211170
|
-
|
|
211171
|
-
|
|
211172
|
-
|
|
211173
|
-
|
|
211174
|
-
|
|
211175
|
-
|
|
211176
|
-
|
|
211177
|
-
|
|
211178
|
-
|
|
211179
|
-
|
|
211180
|
-
|
|
211181
|
-
|
|
211182
|
-
|
|
211183
|
-
|
|
211184
|
-
|
|
211185
|
-
|
|
211186
|
-
|
|
211187
|
-
|
|
211188
|
-
|
|
211189
|
-
|
|
211190
|
-
|
|
211191
|
-
|
|
211192
|
-
|
|
211193
|
-
|
|
211194
|
-
|
|
211195
|
-
|
|
211196
|
-
|
|
211197
|
-
|
|
211198
|
-
|
|
211199
|
-
|
|
211200
|
-
|
|
211201
|
-
|
|
211202
|
-
|
|
211203
|
-
|
|
211204
|
-
|
|
211205
|
-
|
|
211206
|
-
|
|
211207
|
-
|
|
211208
|
-
|
|
211209
|
-
|
|
211210
|
-
|
|
211211
|
-
|
|
211212
|
-
|
|
211213
|
-
|
|
211214
|
-
|
|
211215
|
-
|
|
211216
|
-
|
|
211217
|
-
|
|
211218
|
-
|
|
211219
|
-
|
|
211220
|
-
|
|
211221
|
-
|
|
211222
|
-
|
|
211223
|
-
|
|
211224
|
-
|
|
211225
|
-
|
|
211226
|
-
|
|
211227
|
-
|
|
211228
|
-
|
|
211229
|
-
|
|
211230
|
-
|
|
211231
|
-
|
|
211232
|
-
|
|
211233
|
-
|
|
211234
|
-
|
|
211235
|
-
|
|
211236
|
-
|
|
211237
|
-
|
|
211238
|
-
|
|
211239
|
-
|
|
211240
|
-
|
|
211241
|
-
|
|
211242
|
-
|
|
211243
|
-
|
|
211244
|
-
|
|
211245
|
-
|
|
211246
|
-
|
|
211247
|
-
|
|
211248
|
-
|
|
211249
|
-
|
|
211250
|
-
|
|
211251
|
-
|
|
211252
|
-
|
|
211253
|
-
|
|
211254
|
-
|
|
211255
|
-
|
|
211256
|
-
|
|
211257
|
-
|
|
211258
|
-
|
|
211259
|
-
|
|
211260
|
-
|
|
211261
|
-
|
|
211262
|
-
|
|
211263
|
-
|
|
211264
|
-
|
|
211265
|
-
|
|
211266
|
-
|
|
211267
|
-
|
|
211268
|
-
|
|
211269
|
-
|
|
211270
|
-
|
|
211271
|
-
|
|
211272
|
-
|
|
211273
|
-
|
|
211274
|
-
|
|
211275
|
-
|
|
211276
|
-
|
|
211277
|
-
|
|
211278
|
-
|
|
211279
|
-
|
|
211280
|
-
|
|
211281
|
-
|
|
211282
|
-
|
|
211283
|
-
|
|
211284
|
-
|
|
211285
|
-
|
|
211286
|
-
|
|
211287
|
-
|
|
211288
|
-
|
|
211289
|
-
|
|
211290
|
-
|
|
211291
|
-
|
|
211292
|
-
|
|
211293
|
-
|
|
211294
|
-
|
|
211295
|
-
|
|
211296
|
-
|
|
211297
|
-
|
|
211298
|
-
|
|
211299
|
-
|
|
211300
|
-
|
|
211301
|
-
|
|
211302
|
-
|
|
211303
|
-
|
|
211304
|
-
|
|
211305
|
-
if (event.source === gameFrame.contentWindow) {
|
|
211306
|
-
const { type, ...payload } = event.data || {}
|
|
211307
|
-
if (type && type.startsWith('PLAYCADEMY_')) {
|
|
211308
|
-
logIfDebug(
|
|
211309
|
-
'[PlaycademyDevShell] Received message from game:',
|
|
211310
|
-
type,
|
|
211311
|
-
payload,
|
|
211312
|
-
)
|
|
211313
|
-
// Bridge the message to the local context using CustomEvent
|
|
211314
|
-
messaging.send(type, payload)
|
|
211315
|
-
}
|
|
211316
|
-
}
|
|
211317
|
-
})
|
|
211456
|
+
// ../sandbox/dist/constants.js
|
|
211457
|
+
var now2 = new Date;
|
|
211458
|
+
var DEMO_USER_IDS2 = {
|
|
211459
|
+
player: "00000000-0000-0000-0000-000000000001",
|
|
211460
|
+
developer: "00000000-0000-0000-0000-000000000002",
|
|
211461
|
+
admin: "00000000-0000-0000-0000-000000000003",
|
|
211462
|
+
pendingDeveloper: "00000000-0000-0000-0000-000000000004",
|
|
211463
|
+
unverifiedPlayer: "00000000-0000-0000-0000-000000000005"
|
|
211464
|
+
};
|
|
211465
|
+
var DEMO_USERS2 = {
|
|
211466
|
+
admin: {
|
|
211467
|
+
id: DEMO_USER_IDS2.admin,
|
|
211468
|
+
name: "Admin User",
|
|
211469
|
+
username: "admin_user",
|
|
211470
|
+
email: "admin@playcademy.com",
|
|
211471
|
+
emailVerified: true,
|
|
211472
|
+
image: null,
|
|
211473
|
+
role: "admin",
|
|
211474
|
+
developerStatus: "approved",
|
|
211475
|
+
createdAt: now2,
|
|
211476
|
+
updatedAt: now2
|
|
211477
|
+
},
|
|
211478
|
+
player: {
|
|
211479
|
+
id: DEMO_USER_IDS2.player,
|
|
211480
|
+
name: "Player User",
|
|
211481
|
+
username: "player_user",
|
|
211482
|
+
email: "player@playcademy.com",
|
|
211483
|
+
emailVerified: true,
|
|
211484
|
+
image: null,
|
|
211485
|
+
role: "player",
|
|
211486
|
+
developerStatus: "none",
|
|
211487
|
+
createdAt: now2,
|
|
211488
|
+
updatedAt: now2
|
|
211489
|
+
},
|
|
211490
|
+
developer: {
|
|
211491
|
+
id: DEMO_USER_IDS2.developer,
|
|
211492
|
+
name: "Developer User",
|
|
211493
|
+
username: "developer_user",
|
|
211494
|
+
email: "developer@playcademy.com",
|
|
211495
|
+
emailVerified: true,
|
|
211496
|
+
image: null,
|
|
211497
|
+
role: "developer",
|
|
211498
|
+
developerStatus: "approved",
|
|
211499
|
+
createdAt: now2,
|
|
211500
|
+
updatedAt: now2
|
|
211501
|
+
},
|
|
211502
|
+
pendingDeveloper: {
|
|
211503
|
+
id: DEMO_USER_IDS2.pendingDeveloper,
|
|
211504
|
+
name: "Pending Developer",
|
|
211505
|
+
username: "pending_dev",
|
|
211506
|
+
email: "pending@playcademy.com",
|
|
211507
|
+
emailVerified: true,
|
|
211508
|
+
image: null,
|
|
211509
|
+
role: "developer",
|
|
211510
|
+
developerStatus: "pending",
|
|
211511
|
+
createdAt: now2,
|
|
211512
|
+
updatedAt: now2
|
|
211513
|
+
},
|
|
211514
|
+
unverifiedPlayer: {
|
|
211515
|
+
id: DEMO_USER_IDS2.unverifiedPlayer,
|
|
211516
|
+
name: "Unverified Player",
|
|
211517
|
+
username: "unverified_player",
|
|
211518
|
+
email: "unverified@playcademy.com",
|
|
211519
|
+
emailVerified: false,
|
|
211520
|
+
image: null,
|
|
211521
|
+
role: "player",
|
|
211522
|
+
developerStatus: "none",
|
|
211523
|
+
createdAt: now2,
|
|
211524
|
+
updatedAt: now2
|
|
211525
|
+
}
|
|
211526
|
+
};
|
|
211527
|
+
var DEMO_USER2 = DEMO_USERS2.player;
|
|
211528
|
+
var DEMO_TOKENS2 = {
|
|
211529
|
+
"sandbox-demo-token": DEMO_USERS2.player,
|
|
211530
|
+
"sandbox-admin-token": DEMO_USERS2.admin,
|
|
211531
|
+
"sandbox-player-token": DEMO_USERS2.player,
|
|
211532
|
+
"sandbox-developer-token": DEMO_USERS2.developer,
|
|
211533
|
+
"sandbox-pending-dev-token": DEMO_USERS2.pendingDeveloper,
|
|
211534
|
+
"sandbox-unverified-token": DEMO_USERS2.unverifiedPlayer,
|
|
211535
|
+
"mock-game-token-for-local-dev": DEMO_USERS2.player
|
|
211536
|
+
};
|
|
211537
|
+
var DEMO_ITEM_IDS2 = {
|
|
211538
|
+
playcademyCredits: "10000000-0000-0000-0000-000000000001",
|
|
211539
|
+
foundingMemberBadge: "10000000-0000-0000-0000-000000000002",
|
|
211540
|
+
earlyAdopterBadge: "10000000-0000-0000-0000-000000000003",
|
|
211541
|
+
firstGameBadge: "10000000-0000-0000-0000-000000000004",
|
|
211542
|
+
commonSword: "10000000-0000-0000-0000-000000000005",
|
|
211543
|
+
smallHealthPotion: "10000000-0000-0000-0000-000000000006",
|
|
211544
|
+
smallBackpack: "10000000-0000-0000-0000-000000000007"
|
|
211545
|
+
};
|
|
211546
|
+
var PLAYCADEMY_CREDITS_ID2 = DEMO_ITEM_IDS2.playcademyCredits;
|
|
211547
|
+
var SAMPLE_ITEMS2 = [
|
|
211548
|
+
{
|
|
211549
|
+
id: PLAYCADEMY_CREDITS_ID2,
|
|
211550
|
+
slug: "PLAYCADEMY_CREDITS",
|
|
211551
|
+
gameId: null,
|
|
211552
|
+
displayName: "PLAYCADEMY credits",
|
|
211553
|
+
description: "The main currency used across PLAYCADEMY.",
|
|
211554
|
+
type: "currency",
|
|
211555
|
+
isPlaceable: false,
|
|
211556
|
+
imageUrl: "http://playcademy-sandbox.local/playcademy-credit.png",
|
|
211557
|
+
metadata: {
|
|
211558
|
+
rarity: "common"
|
|
211559
|
+
}
|
|
211560
|
+
},
|
|
211561
|
+
{
|
|
211562
|
+
id: DEMO_ITEM_IDS2.foundingMemberBadge,
|
|
211563
|
+
slug: "FOUNDING_MEMBER_BADGE",
|
|
211564
|
+
gameId: null,
|
|
211565
|
+
displayName: "Founding Member Badge",
|
|
211566
|
+
description: "Reserved for founding core team of the PLAYCADEMY platform.",
|
|
211567
|
+
type: "badge",
|
|
211568
|
+
isPlaceable: false,
|
|
211569
|
+
imageUrl: null,
|
|
211570
|
+
metadata: {
|
|
211571
|
+
rarity: "legendary"
|
|
211572
|
+
}
|
|
211573
|
+
},
|
|
211574
|
+
{
|
|
211575
|
+
id: DEMO_ITEM_IDS2.earlyAdopterBadge,
|
|
211576
|
+
slug: "EARLY_ADOPTER_BADGE",
|
|
211577
|
+
gameId: null,
|
|
211578
|
+
displayName: "Early Adopter Badge",
|
|
211579
|
+
description: "Awarded to users who joined during the beta phase.",
|
|
211580
|
+
type: "badge",
|
|
211581
|
+
isPlaceable: false,
|
|
211582
|
+
imageUrl: null,
|
|
211583
|
+
metadata: {
|
|
211584
|
+
rarity: "epic"
|
|
211585
|
+
}
|
|
211586
|
+
},
|
|
211587
|
+
{
|
|
211588
|
+
id: DEMO_ITEM_IDS2.firstGameBadge,
|
|
211589
|
+
slug: "FIRST_GAME_BADGE",
|
|
211590
|
+
gameId: null,
|
|
211591
|
+
displayName: "First Game Played",
|
|
211592
|
+
description: "Awarded for playing your first game in the Playcademy platform.",
|
|
211593
|
+
type: "badge",
|
|
211594
|
+
isPlaceable: false,
|
|
211595
|
+
imageUrl: "http://playcademy-sandbox.local/first-game-badge.png",
|
|
211596
|
+
metadata: {
|
|
211597
|
+
rarity: "uncommon"
|
|
211598
|
+
}
|
|
211599
|
+
},
|
|
211600
|
+
{
|
|
211601
|
+
id: DEMO_ITEM_IDS2.commonSword,
|
|
211602
|
+
slug: "COMMON_SWORD",
|
|
211603
|
+
gameId: null,
|
|
211604
|
+
displayName: "Common Sword",
|
|
211605
|
+
description: "A basic sword, good for beginners.",
|
|
211606
|
+
type: "unlock",
|
|
211607
|
+
isPlaceable: false,
|
|
211608
|
+
imageUrl: "http://playcademy-sandbox.local/common-sword.png",
|
|
211609
|
+
metadata: undefined
|
|
211610
|
+
},
|
|
211611
|
+
{
|
|
211612
|
+
id: DEMO_ITEM_IDS2.smallHealthPotion,
|
|
211613
|
+
slug: "SMALL_HEALTH_POTION",
|
|
211614
|
+
gameId: null,
|
|
211615
|
+
displayName: "Small Health Potion",
|
|
211616
|
+
description: "Restores a small amount of health.",
|
|
211617
|
+
type: "other",
|
|
211618
|
+
isPlaceable: false,
|
|
211619
|
+
imageUrl: "http://playcademy-sandbox.local/small-health-potion.png",
|
|
211620
|
+
metadata: undefined
|
|
211621
|
+
},
|
|
211622
|
+
{
|
|
211623
|
+
id: DEMO_ITEM_IDS2.smallBackpack,
|
|
211624
|
+
slug: "SMALL_BACKPACK",
|
|
211625
|
+
gameId: null,
|
|
211626
|
+
displayName: "Small Backpack",
|
|
211627
|
+
description: "Increases your inventory capacity by 5 slots.",
|
|
211628
|
+
type: "upgrade",
|
|
211629
|
+
isPlaceable: false,
|
|
211630
|
+
imageUrl: "http://playcademy-sandbox.local/small-backpack.png",
|
|
211631
|
+
metadata: undefined
|
|
211632
|
+
}
|
|
211633
|
+
];
|
|
211634
|
+
var SAMPLE_INVENTORY2 = [
|
|
211635
|
+
{
|
|
211636
|
+
id: "20000000-0000-0000-0000-000000000001",
|
|
211637
|
+
userId: DEMO_USER2.id,
|
|
211638
|
+
itemId: PLAYCADEMY_CREDITS_ID2,
|
|
211639
|
+
quantity: 1000
|
|
211640
|
+
}
|
|
211641
|
+
];
|
|
211318
211642
|
|
|
211319
|
-
|
|
211320
|
-
|
|
211321
|
-
|
|
211322
|
-
|
|
211323
|
-
|
|
211324
|
-
|
|
211325
|
-
|
|
211643
|
+
// src/lib/sandbox/token.ts
|
|
211644
|
+
var ROLE_TO_USER_ID = {
|
|
211645
|
+
player: DEMO_USER_IDS2.player,
|
|
211646
|
+
developer: DEMO_USER_IDS2.developer,
|
|
211647
|
+
admin: DEMO_USER_IDS2.admin
|
|
211648
|
+
};
|
|
211649
|
+
function createSandboxGameToken(gameSlug, role = "player") {
|
|
211650
|
+
const header = { alg: "none", typ: "sandbox" };
|
|
211651
|
+
const payload = {
|
|
211652
|
+
sub: gameSlug,
|
|
211653
|
+
uid: ROLE_TO_USER_ID[role],
|
|
211654
|
+
iat: Math.floor(Date.now() / 1000)
|
|
211655
|
+
};
|
|
211656
|
+
const encode3 = (obj) => btoa(JSON.stringify(obj));
|
|
211657
|
+
return `${encode3(header)}.${encode3(payload)}.sandbox`;
|
|
211658
|
+
}
|
|
211326
211659
|
|
|
211327
|
-
// src/shells/shell
|
|
211328
|
-
var
|
|
211660
|
+
// src/shells/shell.html
|
|
211661
|
+
var shell_default = `<!doctype html>
|
|
211329
211662
|
<html lang="en">
|
|
211330
211663
|
<head>
|
|
211331
211664
|
<meta charset="UTF-8" />
|
|
@@ -211385,16 +211718,16 @@ var shell_with_corner_badge_default = `<!doctype html>
|
|
|
211385
211718
|
<script type="module">
|
|
211386
211719
|
import { MessageEvents, messaging } from '@playcademy/sdk'
|
|
211387
211720
|
|
|
211388
|
-
// Config (injected by vite plugin)
|
|
211389
211721
|
const CONFIG = {
|
|
211390
211722
|
sandboxUrl: '{{SANDBOX_URL}}',
|
|
211391
211723
|
gameId: '{{GAME_ID}}',
|
|
211724
|
+
gameToken: '{{GAME_TOKEN}}',
|
|
211392
211725
|
gameUrl: '{{GAME_URL}}' || undefined,
|
|
211393
211726
|
realtimeUrl: '{{REALTIME_URL}}',
|
|
211394
211727
|
timebackJson: '{{TIMEBACK_DATA}}',
|
|
211728
|
+
hideBadge: '{{HIDE_BADGE}}' === 'true',
|
|
211395
211729
|
}
|
|
211396
211730
|
|
|
211397
|
-
// Parse timeback data (role + enrollments)
|
|
211398
211731
|
const timeback = (() => {
|
|
211399
211732
|
try {
|
|
211400
211733
|
return JSON.parse(CONFIG.timebackJson)
|
|
@@ -211403,18 +211736,19 @@ var shell_with_corner_badge_default = `<!doctype html>
|
|
|
211403
211736
|
}
|
|
211404
211737
|
})()
|
|
211405
211738
|
|
|
211406
|
-
// Elements
|
|
211407
211739
|
const badge = document.getElementById('badge')
|
|
211408
211740
|
const frame = document.getElementById('frame')
|
|
211409
211741
|
|
|
211410
|
-
|
|
211742
|
+
if (CONFIG.hideBadge) {
|
|
211743
|
+
badge.classList.add('hidden')
|
|
211744
|
+
}
|
|
211745
|
+
|
|
211411
211746
|
const log = (...args) => window.PLAYCADEMY_DEBUG && console.log('[DevShell]', ...args)
|
|
211412
211747
|
|
|
211413
|
-
// Check sandbox connection
|
|
211414
211748
|
async function checkSandbox() {
|
|
211415
211749
|
try {
|
|
211416
211750
|
const res = await fetch(\`\${CONFIG.sandboxUrl}/api/users/me\`, {
|
|
211417
|
-
headers: { Authorization:
|
|
211751
|
+
headers: { Authorization: \`Bearer \${CONFIG.gameToken}\` },
|
|
211418
211752
|
})
|
|
211419
211753
|
if (!res.ok) throw new Error('Sandbox unavailable')
|
|
211420
211754
|
badge.classList.remove('offline')
|
|
@@ -211428,14 +211762,13 @@ var shell_with_corner_badge_default = `<!doctype html>
|
|
|
211428
211762
|
}
|
|
211429
211763
|
}
|
|
211430
211764
|
|
|
211431
|
-
// Init handshake with game iframe
|
|
211432
211765
|
function initHandshake(timebackData) {
|
|
211433
211766
|
const payload = {
|
|
211434
211767
|
baseUrl: CONFIG.sandboxUrl,
|
|
211435
211768
|
gameUrl: CONFIG.gameUrl,
|
|
211436
211769
|
gameId: CONFIG.gameId,
|
|
211437
211770
|
realtimeUrl: CONFIG.realtimeUrl,
|
|
211438
|
-
token:
|
|
211771
|
+
token: CONFIG.gameToken,
|
|
211439
211772
|
timeback: timebackData,
|
|
211440
211773
|
}
|
|
211441
211774
|
|
|
@@ -211474,7 +211807,6 @@ var shell_with_corner_badge_default = `<!doctype html>
|
|
|
211474
211807
|
frame.src = '/'
|
|
211475
211808
|
}
|
|
211476
211809
|
|
|
211477
|
-
// Bridge messages from game to shell context
|
|
211478
211810
|
window.addEventListener('message', e => {
|
|
211479
211811
|
if (e.source !== frame.contentWindow) return
|
|
211480
211812
|
const { type, ...payload } = e.data || {}
|
|
@@ -211484,7 +211816,6 @@ var shell_with_corner_badge_default = `<!doctype html>
|
|
|
211484
211816
|
}
|
|
211485
211817
|
})
|
|
211486
211818
|
|
|
211487
|
-
// Start
|
|
211488
211819
|
checkSandbox().then(() => initHandshake(timeback))
|
|
211489
211820
|
</script>
|
|
211490
211821
|
</body>
|
|
@@ -211492,10 +211823,11 @@ var shell_with_corner_badge_default = `<!doctype html>
|
|
|
211492
211823
|
`;
|
|
211493
211824
|
|
|
211494
211825
|
// src/server/middleware.ts
|
|
211495
|
-
function generateLoaderHTML(sandboxUrl,
|
|
211496
|
-
const
|
|
211497
|
-
const
|
|
211498
|
-
|
|
211826
|
+
function generateLoaderHTML(sandboxUrl, gameSlug, realtimeUrl, options, gameUrl) {
|
|
211827
|
+
const timebackJson = generateTimebackJson(options.timeback);
|
|
211828
|
+
const platformRole = getPlatformRoleOverride() ?? "player";
|
|
211829
|
+
const gameToken = createSandboxGameToken(gameSlug, platformRole);
|
|
211830
|
+
return shell_default.replace(/{{SANDBOX_URL}}/g, sandboxUrl).replace(/{{GAME_ID}}/g, gameSlug).replace(/{{GAME_TOKEN}}/g, gameToken).replace(/{{REALTIME_URL}}/g, realtimeUrl).replace(/{{GAME_URL}}/g, gameUrl || "").replace(/{{TIMEBACK_DATA}}/g, timebackJson).replace(/{{HIDE_BADGE}}/g, String(options.hideBadge));
|
|
211499
211831
|
}
|
|
211500
211832
|
function devServerMiddleware(server, sandbox, gameUrl, options) {
|
|
211501
211833
|
server.middlewares.use("/", (req, res, next) => {
|
|
@@ -211517,69 +211849,166 @@ function devServerMiddleware(server, sandbox, gameUrl, options) {
|
|
|
211517
211849
|
});
|
|
211518
211850
|
}
|
|
211519
211851
|
|
|
211520
|
-
// src/server/
|
|
211521
|
-
|
|
211522
|
-
|
|
211523
|
-
const now2 = new Date;
|
|
211524
|
-
const hours = now2.getHours();
|
|
211525
|
-
const minutes = now2.getMinutes().toString().padStart(2, "0");
|
|
211526
|
-
const seconds = now2.getSeconds().toString().padStart(2, "0");
|
|
211527
|
-
const ampm = hours >= 12 ? "PM" : "AM";
|
|
211528
|
-
const displayHours = hours % 12 || 12;
|
|
211529
|
-
return dim4(`${displayHours}:${minutes}:${seconds} ${ampm}`);
|
|
211530
|
-
}
|
|
211531
|
-
async function recreateSandboxDatabase(options) {
|
|
211852
|
+
// src/server/recreate-sandbox.ts
|
|
211853
|
+
async function recreateSandbox(options) {
|
|
211854
|
+
const { viteConfig, platformModeOptions } = options;
|
|
211532
211855
|
const currentMode = getCurrentMode();
|
|
211533
211856
|
const viteServer = getViteServerRef();
|
|
211857
|
+
const prefix2 = createLogPrefix("playcademy", "sandbox");
|
|
211534
211858
|
if (!viteServer) {
|
|
211535
|
-
|
|
211536
|
-
return;
|
|
211859
|
+
viteConfig.logger.error(`${prefix2} ${import_picocolors6.red("Cannot recreate sandbox database: no Vite server reference")}`);
|
|
211860
|
+
return { success: false, error: "No Vite server reference" };
|
|
211537
211861
|
}
|
|
211538
211862
|
if (currentMode !== "platform") {
|
|
211539
|
-
|
|
211540
|
-
return;
|
|
211863
|
+
viteConfig.logger.warn(`${prefix2} ${import_picocolors6.yellow("can only recreate sandbox database in platform mode (m + enter)")}`);
|
|
211864
|
+
return { success: false, error: "Not in platform mode" };
|
|
211541
211865
|
}
|
|
211542
|
-
|
|
211866
|
+
viteConfig.logger.info(`${prefix2} recreating database...`);
|
|
211543
211867
|
if (serverState.sandbox) {
|
|
211544
211868
|
serverState.sandbox.cleanup();
|
|
211545
211869
|
serverState.sandbox = null;
|
|
211546
211870
|
}
|
|
211547
211871
|
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
211548
|
-
const
|
|
211549
|
-
|
|
211550
|
-
|
|
211551
|
-
|
|
211552
|
-
|
|
211872
|
+
const timebackDisabled = platformModeOptions.timeback === false;
|
|
211873
|
+
const timebackOptions = platformModeOptions.timeback || undefined;
|
|
211874
|
+
const sandbox = await startSandbox(viteConfig, platformModeOptions.startSandbox, {
|
|
211875
|
+
port: platformModeOptions.sandboxPort,
|
|
211876
|
+
verbose: platformModeOptions.verbose,
|
|
211877
|
+
logLevel: platformModeOptions.logLevel,
|
|
211878
|
+
customUrl: platformModeOptions.sandboxUrl,
|
|
211553
211879
|
recreateDb: true,
|
|
211554
|
-
seed:
|
|
211555
|
-
memoryOnly:
|
|
211556
|
-
databasePath:
|
|
211557
|
-
realtimeEnabled:
|
|
211558
|
-
realtimePort:
|
|
211559
|
-
|
|
211880
|
+
seed: platformModeOptions.seed,
|
|
211881
|
+
memoryOnly: platformModeOptions.memoryOnly,
|
|
211882
|
+
databasePath: platformModeOptions.databasePath,
|
|
211883
|
+
realtimeEnabled: platformModeOptions.realtimeEnabled,
|
|
211884
|
+
realtimePort: platformModeOptions.realtimePort,
|
|
211885
|
+
timebackOptions: timebackDisabled ? false : platformModeOptions.timeback
|
|
211560
211886
|
});
|
|
211561
211887
|
serverState.sandbox = sandbox;
|
|
211562
211888
|
if (sandbox.project && serverState.backend) {
|
|
211563
211889
|
const gameUrl = `http://localhost:${serverState.backend.port}`;
|
|
211564
211890
|
devServerMiddleware(viteServer, sandbox, gameUrl, {
|
|
211565
|
-
|
|
211566
|
-
timeback:
|
|
211891
|
+
hideBadge: platformModeOptions.hideBadge,
|
|
211892
|
+
timeback: timebackDisabled ? undefined : {
|
|
211893
|
+
baseCourses: sandbox.project.timebackCourses ?? [],
|
|
211894
|
+
overrides: timebackOptions
|
|
211895
|
+
}
|
|
211567
211896
|
});
|
|
211568
211897
|
}
|
|
211569
|
-
|
|
211898
|
+
viteConfig.logger.info(`${prefix2} ${import_picocolors6.green("database recreated")}`);
|
|
211899
|
+
return { success: true };
|
|
211900
|
+
}
|
|
211901
|
+
|
|
211902
|
+
// src/server/config-watcher.ts
|
|
211903
|
+
var DEBOUNCE_MS = 500;
|
|
211904
|
+
var VITE_CONFIG_NAMES = ["vite.config.ts", "vite.config.js", "vite.config.mjs"];
|
|
211905
|
+
var debounceTimer = null;
|
|
211906
|
+
function findExistingFiles(projectRoot, fileNames) {
|
|
211907
|
+
return fileNames.map((name3) => path6.join(projectRoot, name3)).filter((file) => fs8.existsSync(file));
|
|
211908
|
+
}
|
|
211909
|
+
function createChangeHandler(server, viteConfig, platformModeOptions, watchedFiles) {
|
|
211910
|
+
return async (changedPath) => {
|
|
211911
|
+
const isWatchedFile = watchedFiles.some((file) => path6.resolve(changedPath) === path6.resolve(file));
|
|
211912
|
+
if (!isWatchedFile)
|
|
211913
|
+
return;
|
|
211914
|
+
if (getCurrentMode() !== "platform")
|
|
211915
|
+
return;
|
|
211916
|
+
if (debounceTimer)
|
|
211917
|
+
clearTimeout(debounceTimer);
|
|
211918
|
+
debounceTimer = setTimeout(async () => {
|
|
211919
|
+
await recreateSandbox({
|
|
211920
|
+
viteConfig,
|
|
211921
|
+
platformModeOptions
|
|
211922
|
+
});
|
|
211923
|
+
server.ws.send({ type: "full-reload" });
|
|
211924
|
+
}, DEBOUNCE_MS);
|
|
211925
|
+
};
|
|
211926
|
+
}
|
|
211927
|
+
function setupConfigWatcher(server, viteConfig, platformModeOptions) {
|
|
211928
|
+
const projectRoot = viteConfig.root;
|
|
211929
|
+
const playcademyConfigs = findExistingFiles(projectRoot, CONFIG_FILE_NAMES);
|
|
211930
|
+
const viteConfigs = findExistingFiles(projectRoot, VITE_CONFIG_NAMES);
|
|
211931
|
+
const allConfigFiles = [...playcademyConfigs, ...viteConfigs];
|
|
211932
|
+
if (allConfigFiles.length === 0)
|
|
211933
|
+
return;
|
|
211934
|
+
for (const configFile of allConfigFiles) {
|
|
211935
|
+
server.watcher.add(configFile);
|
|
211936
|
+
}
|
|
211937
|
+
server.watcher.on("change", createChangeHandler(server, viteConfig, platformModeOptions, allConfigFiles));
|
|
211938
|
+
}
|
|
211939
|
+
|
|
211940
|
+
// src/server/hotkeys/cycle-platform-role.ts
|
|
211941
|
+
var import_picocolors7 = __toESM(require_picocolors(), 1);
|
|
211942
|
+
|
|
211943
|
+
// src/types/internal.ts
|
|
211944
|
+
var TIMEBACK_ROLES = ["student", "parent", "teacher", "administrator"];
|
|
211945
|
+
var PLATFORM_ROLES = ["player", "developer", "admin"];
|
|
211946
|
+
// src/server/hotkeys/cycle-platform-role.ts
|
|
211947
|
+
function cyclePlatformRole(logger) {
|
|
211948
|
+
const currentRole = getPlatformRoleOverride() ?? "player";
|
|
211949
|
+
const currentIndex = PLATFORM_ROLES.indexOf(currentRole);
|
|
211950
|
+
const nextIndex = (currentIndex + 1) % PLATFORM_ROLES.length;
|
|
211951
|
+
const nextRole = PLATFORM_ROLES[nextIndex];
|
|
211952
|
+
const prefix2 = createLogPrefix("playcademy", "user");
|
|
211953
|
+
setPlatformRoleOverride(nextRole);
|
|
211954
|
+
logger.info(`${prefix2} ${import_picocolors7.red(currentRole)} → ${import_picocolors7.green(nextRole)}`);
|
|
211955
|
+
if (getViteServerRef()) {
|
|
211956
|
+
getViteServerRef()?.ws.send({ type: "full-reload", path: "*" });
|
|
211957
|
+
} else {
|
|
211958
|
+
logger.warn(`${prefix2} ${import_picocolors7.yellow("Cannot cycle platform role: no Vite server reference")}`);
|
|
211959
|
+
}
|
|
211960
|
+
}
|
|
211961
|
+
var cyclePlatformRoleHotkey = (options) => ({
|
|
211962
|
+
key: "p",
|
|
211963
|
+
description: `${import_picocolors7.cyan(import_picocolors7.bold("[playcademy]"))} cycle platform role`,
|
|
211964
|
+
action: () => cyclePlatformRole(options.viteConfig.logger)
|
|
211965
|
+
});
|
|
211966
|
+
|
|
211967
|
+
// src/server/hotkeys/cycle-timeback-role.ts
|
|
211968
|
+
var import_picocolors8 = __toESM(require_picocolors(), 1);
|
|
211969
|
+
var { bold: bold4, cyan: cyan4, green: green4, red: red3, yellow: yellow3 } = import_picocolors8.default;
|
|
211970
|
+
function cycleTimebackRole(logger) {
|
|
211971
|
+
const currentRole = getTimebackRoleOverride() ?? "student";
|
|
211972
|
+
const currentIndex = TIMEBACK_ROLES.indexOf(currentRole);
|
|
211973
|
+
const nextIndex = (currentIndex + 1) % TIMEBACK_ROLES.length;
|
|
211974
|
+
const nextRole = TIMEBACK_ROLES[nextIndex];
|
|
211975
|
+
setTimebackRoleOverride(nextRole);
|
|
211976
|
+
getSandboxRef()?.setRole?.(nextRole);
|
|
211977
|
+
const prefix2 = createLogPrefix("playcademy", "timeback");
|
|
211978
|
+
logger.info(`${prefix2} ${red3(currentRole)} → ${green4(nextRole)}`);
|
|
211979
|
+
if (getViteServerRef()) {
|
|
211980
|
+
getViteServerRef()?.ws.send({ type: "full-reload", path: "*" });
|
|
211981
|
+
} else {
|
|
211982
|
+
logger.warn(`${prefix2} ${yellow3("Cannot cycle TimeBack role: no Vite server reference")}`);
|
|
211983
|
+
}
|
|
211984
|
+
}
|
|
211985
|
+
var cycleTimebackRoleHotkey = (options) => ({
|
|
211986
|
+
key: "t",
|
|
211987
|
+
description: `${cyan4(bold4("[playcademy]"))} cycle Timeback role`,
|
|
211988
|
+
action: () => cycleTimebackRole(options.viteConfig.logger)
|
|
211989
|
+
});
|
|
211990
|
+
|
|
211991
|
+
// src/server/hotkeys/recreate-database.ts
|
|
211992
|
+
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
211993
|
+
var { bold: bold5, cyan: cyan5 } = import_picocolors9.default;
|
|
211994
|
+
async function recreateSandboxDatabase(options) {
|
|
211995
|
+
await recreateSandbox({
|
|
211996
|
+
viteConfig: options.viteConfig,
|
|
211997
|
+
platformModeOptions: options.platformModeOptions
|
|
211998
|
+
});
|
|
211570
211999
|
}
|
|
211571
212000
|
var recreateDatabaseHotkey = (options) => ({
|
|
211572
212001
|
key: "d",
|
|
211573
|
-
description: "recreate sandbox database
|
|
212002
|
+
description: `${cyan5(bold5("[playcademy]"))} recreate sandbox database`,
|
|
211574
212003
|
action: () => recreateSandboxDatabase(options)
|
|
211575
212004
|
});
|
|
211576
212005
|
|
|
211577
212006
|
// src/server/hotkeys/toggle-mode.ts
|
|
211578
|
-
var
|
|
212007
|
+
var import_picocolors11 = __toESM(require_picocolors(), 1);
|
|
211579
212008
|
// package.json
|
|
211580
212009
|
var package_default2 = {
|
|
211581
212010
|
name: "@playcademy/vite-plugin",
|
|
211582
|
-
version: "0.
|
|
212011
|
+
version: "0.2.0",
|
|
211583
212012
|
type: "module",
|
|
211584
212013
|
exports: {
|
|
211585
212014
|
".": {
|
|
@@ -211598,6 +212027,7 @@ var package_default2 = {
|
|
|
211598
212027
|
pub: "bun publish.ts"
|
|
211599
212028
|
},
|
|
211600
212029
|
dependencies: {
|
|
212030
|
+
"@playcademy/utils": "workspace:*",
|
|
211601
212031
|
archiver: "^7.0.1",
|
|
211602
212032
|
picocolors: "^1.1.1",
|
|
211603
212033
|
playcademy: "workspace:*"
|
|
@@ -211622,27 +212052,29 @@ import {
|
|
|
211622
212052
|
} from "playcademy/utils";
|
|
211623
212053
|
|
|
211624
212054
|
// src/lib/backend/hot-reload.ts
|
|
211625
|
-
var
|
|
211626
|
-
|
|
211627
|
-
if (!changedPath)
|
|
211628
|
-
return;
|
|
211629
|
-
if (changedPath.includes("/api/")) {
|
|
211630
|
-
return changedPath.substring(changedPath.indexOf("/api/"));
|
|
211631
|
-
}
|
|
211632
|
-
return changedPath;
|
|
211633
|
-
}
|
|
212055
|
+
var import_picocolors10 = __toESM(require_picocolors(), 1);
|
|
212056
|
+
import path7 from "node:path";
|
|
211634
212057
|
function createHotReloadCallbacks(viteConfig) {
|
|
212058
|
+
const formatPath = (changedPath) => {
|
|
212059
|
+
if (!changedPath)
|
|
212060
|
+
return;
|
|
212061
|
+
if (changedPath.startsWith(viteConfig.root)) {
|
|
212062
|
+
return path7.relative(viteConfig.root, changedPath);
|
|
212063
|
+
}
|
|
212064
|
+
return changedPath;
|
|
212065
|
+
};
|
|
212066
|
+
const prefix2 = createLogPrefix("playcademy", "backend");
|
|
211635
212067
|
return {
|
|
211636
212068
|
onSuccess: (changedPath) => {
|
|
211637
|
-
const relativePath =
|
|
212069
|
+
const relativePath = formatPath(changedPath);
|
|
211638
212070
|
if (relativePath) {
|
|
211639
|
-
viteConfig.logger.info(`${
|
|
212071
|
+
viteConfig.logger.info(`${prefix2} ${import_picocolors10.green("hmr update")} ${import_picocolors10.dim(relativePath)}`);
|
|
211640
212072
|
} else {
|
|
211641
|
-
viteConfig.logger.info(
|
|
212073
|
+
viteConfig.logger.info(`${prefix2} reloaded`);
|
|
211642
212074
|
}
|
|
211643
212075
|
},
|
|
211644
212076
|
onError: (error2) => {
|
|
211645
|
-
viteConfig.logger.error(
|
|
212077
|
+
viteConfig.logger.error(`${prefix2} reload failed: ${error2 instanceof Error ? error2.message : String(error2)}`);
|
|
211646
212078
|
}
|
|
211647
212079
|
};
|
|
211648
212080
|
}
|
|
@@ -211679,78 +212111,101 @@ function setupHotReload(serverRef, options) {
|
|
|
211679
212111
|
return () => watcher.close();
|
|
211680
212112
|
}
|
|
211681
212113
|
async function setupCliDevServer(options) {
|
|
211682
|
-
const {
|
|
212114
|
+
const { port, viteConfig, platformUrl, configPath } = options;
|
|
211683
212115
|
const config2 = await tryLoadConfig(viteConfig, configPath);
|
|
211684
212116
|
if (!config2)
|
|
211685
212117
|
return null;
|
|
211686
212118
|
if (!needsCliDevServer(config2))
|
|
211687
212119
|
return null;
|
|
211688
212120
|
try {
|
|
211689
|
-
const
|
|
211690
|
-
|
|
212121
|
+
const serverOptions = {
|
|
212122
|
+
port,
|
|
212123
|
+
config: config2,
|
|
212124
|
+
platformUrl,
|
|
212125
|
+
viteConfig
|
|
212126
|
+
};
|
|
211691
212127
|
const serverRef = { current: await startServer2(serverOptions) };
|
|
211692
212128
|
const stopHotReload = setupHotReload(serverRef, serverOptions);
|
|
211693
212129
|
return {
|
|
211694
212130
|
server: serverRef.current.server,
|
|
211695
212131
|
port: serverRef.current.port,
|
|
211696
212132
|
stopHotReload,
|
|
211697
|
-
cleanup: () =>
|
|
212133
|
+
cleanup: () => {}
|
|
211698
212134
|
};
|
|
211699
212135
|
} catch (error2) {
|
|
211700
|
-
|
|
212136
|
+
const message3 = error2 instanceof Error ? error2.message : String(error2);
|
|
212137
|
+
viteConfig.logger.error(`Failed to start game backend: ${message3}`);
|
|
211701
212138
|
return null;
|
|
211702
212139
|
}
|
|
211703
212140
|
}
|
|
211704
212141
|
// src/server/platform-mode.ts
|
|
212142
|
+
var BANNER_PRINTED = false;
|
|
212143
|
+
function isCycleableRole(role) {
|
|
212144
|
+
return TIMEBACK_ROLES.includes(role);
|
|
212145
|
+
}
|
|
211705
212146
|
async function configurePlatformMode(server, viteConfig, options) {
|
|
212147
|
+
const timebackDisabled = options.timeback === false;
|
|
212148
|
+
const timebackOptions = options.timeback || undefined;
|
|
212149
|
+
if (timebackOptions?.role && isCycleableRole(timebackOptions.role)) {
|
|
212150
|
+
setTimebackRoleOverride(timebackOptions.role);
|
|
212151
|
+
}
|
|
211706
212152
|
const sandbox = await startSandbox(viteConfig, options.startSandbox, {
|
|
212153
|
+
port: options.sandboxPort,
|
|
211707
212154
|
verbose: options.verbose,
|
|
211708
212155
|
logLevel: options.logLevel,
|
|
211709
212156
|
customUrl: options.sandboxUrl,
|
|
211710
|
-
quiet: true,
|
|
211711
212157
|
recreateDb: options.recreateDb,
|
|
211712
212158
|
seed: options.seed,
|
|
211713
212159
|
memoryOnly: options.memoryOnly,
|
|
211714
212160
|
databasePath: options.databasePath,
|
|
211715
212161
|
realtimeEnabled: options.realtimeEnabled,
|
|
211716
212162
|
realtimePort: options.realtimePort,
|
|
211717
|
-
|
|
212163
|
+
timebackOptions: timebackDisabled ? false : options.timeback
|
|
211718
212164
|
});
|
|
211719
212165
|
serverState.sandbox = sandbox;
|
|
211720
212166
|
const backend = await setupCliDevServer({
|
|
211721
|
-
|
|
212167
|
+
port: options.backendPort,
|
|
211722
212168
|
viteConfig,
|
|
211723
212169
|
platformUrl: sandbox.baseUrl,
|
|
211724
212170
|
configPath: options.configPath
|
|
211725
212171
|
});
|
|
211726
212172
|
serverState.backend = backend;
|
|
211727
212173
|
if (sandbox.project) {
|
|
211728
|
-
|
|
212174
|
+
if (!timebackDisabled) {
|
|
212175
|
+
validateTimebackConfig(viteConfig, timebackOptions);
|
|
212176
|
+
}
|
|
211729
212177
|
const gameUrl = backend ? `http://localhost:${backend.port}` : undefined;
|
|
212178
|
+
const timebackContext = timebackDisabled ? undefined : {
|
|
212179
|
+
baseCourses: sandbox.project.timebackCourses ?? [],
|
|
212180
|
+
overrides: timebackOptions
|
|
212181
|
+
};
|
|
211730
212182
|
devServerMiddleware(server, sandbox, gameUrl, {
|
|
211731
|
-
|
|
211732
|
-
timeback:
|
|
212183
|
+
hideBadge: options.hideBadge,
|
|
212184
|
+
timeback: timebackContext
|
|
212185
|
+
});
|
|
212186
|
+
}
|
|
212187
|
+
if (!BANNER_PRINTED) {
|
|
212188
|
+
server.httpServer?.once("listening", () => {
|
|
212189
|
+
setTimeout(async () => {
|
|
212190
|
+
const projectInfo = await extractProjectInfo(viteConfig, timebackOptions);
|
|
212191
|
+
printBanner(viteConfig, {
|
|
212192
|
+
version: package_default2.version,
|
|
212193
|
+
gameName: projectInfo.slug,
|
|
212194
|
+
sandbox: { port: sandbox.port, enabled: true },
|
|
212195
|
+
backend: createBackendBannerOptions(backend?.port, server.config.server.port),
|
|
212196
|
+
realtimePort: sandbox.realtimePort,
|
|
212197
|
+
timeback: createTimebackBannerOptions(sandbox.timebackMode, projectInfo.timebackCourses?.length)
|
|
212198
|
+
});
|
|
212199
|
+
BANNER_PRINTED = true;
|
|
212200
|
+
}, 100);
|
|
211733
212201
|
});
|
|
211734
212202
|
}
|
|
211735
|
-
server.httpServer?.once("listening", () => {
|
|
211736
|
-
setTimeout(async () => {
|
|
211737
|
-
const projectInfo = await extractProjectInfo(viteConfig);
|
|
211738
|
-
const vitePort = server.config.server.port;
|
|
211739
|
-
printBanner(viteConfig, {
|
|
211740
|
-
sandbox: sandbox.port,
|
|
211741
|
-
backend: backend?.port,
|
|
211742
|
-
realtime: sandbox.realtimePort,
|
|
211743
|
-
vite: vitePort
|
|
211744
|
-
}, projectInfo, package_default2.version);
|
|
211745
|
-
}, 100);
|
|
211746
|
-
});
|
|
211747
212203
|
}
|
|
211748
212204
|
|
|
211749
212205
|
// src/server/standalone-mode.ts
|
|
211750
|
-
var import_picocolors9 = __toESM(require_picocolors(), 1);
|
|
211751
212206
|
async function configureStandaloneMode(server, viteConfig, options) {
|
|
211752
212207
|
const backend = await setupCliDevServer({
|
|
211753
|
-
|
|
212208
|
+
port: options.backendPort,
|
|
211754
212209
|
viteConfig,
|
|
211755
212210
|
platformUrl: undefined,
|
|
211756
212211
|
configPath: options.configPath
|
|
@@ -211762,33 +212217,23 @@ async function configureStandaloneMode(server, viteConfig, options) {
|
|
|
211762
212217
|
serverState.backend = backend;
|
|
211763
212218
|
server.httpServer?.once("listening", () => {
|
|
211764
212219
|
setTimeout(() => {
|
|
211765
|
-
viteConfig
|
|
211766
|
-
|
|
211767
|
-
|
|
211768
|
-
|
|
211769
|
-
|
|
211770
|
-
viteConfig.logger.info("");
|
|
212220
|
+
printBanner(viteConfig, {
|
|
212221
|
+
version: package_default2.version,
|
|
212222
|
+
sandbox: { enabled: false },
|
|
212223
|
+
backend: { port: backend.port }
|
|
212224
|
+
});
|
|
211771
212225
|
}, 100);
|
|
211772
212226
|
});
|
|
211773
212227
|
}
|
|
211774
212228
|
|
|
211775
212229
|
// src/server/hotkeys/toggle-mode.ts
|
|
211776
|
-
var { bold: bold5, cyan: cyan4, dim: dim6, green: green4, red: red2 } = import_picocolors10.default;
|
|
211777
|
-
function formatTimestamp4() {
|
|
211778
|
-
const now2 = new Date;
|
|
211779
|
-
const hours = now2.getHours();
|
|
211780
|
-
const minutes = now2.getMinutes().toString().padStart(2, "0");
|
|
211781
|
-
const seconds = now2.getSeconds().toString().padStart(2, "0");
|
|
211782
|
-
const ampm = hours >= 12 ? "PM" : "AM";
|
|
211783
|
-
const displayHours = hours % 12 || 12;
|
|
211784
|
-
return dim6(`${displayHours}:${minutes}:${seconds} ${ampm}`);
|
|
211785
|
-
}
|
|
211786
212230
|
async function toggleMode(options) {
|
|
211787
212231
|
const currentMode = getCurrentMode();
|
|
211788
212232
|
const newMode = currentMode === "platform" ? "standalone" : "platform";
|
|
211789
212233
|
const viteServer = getViteServerRef();
|
|
212234
|
+
const prefix2 = createLogPrefix("playcademy");
|
|
211790
212235
|
if (!viteServer) {
|
|
211791
|
-
options.viteConfig.logger.error(`${
|
|
212236
|
+
options.viteConfig.logger.error(`${prefix2} ${import_picocolors11.red("Cannot toggle mode: no Vite server reference")}`);
|
|
211792
212237
|
return;
|
|
211793
212238
|
}
|
|
211794
212239
|
await cleanupServers();
|
|
@@ -211796,17 +212241,17 @@ async function toggleMode(options) {
|
|
|
211796
212241
|
setCurrentMode(newMode);
|
|
211797
212242
|
if (newMode === "standalone") {
|
|
211798
212243
|
await configureStandaloneMode(viteServer, options.viteConfig, {
|
|
211799
|
-
|
|
212244
|
+
backendPort: options.platformModeOptions.backendPort,
|
|
211800
212245
|
configPath: options.platformModeOptions.configPath
|
|
211801
212246
|
});
|
|
211802
212247
|
} else {
|
|
211803
212248
|
await configurePlatformMode(viteServer, options.viteConfig, options.platformModeOptions);
|
|
211804
212249
|
}
|
|
211805
|
-
options.viteConfig.logger.info(`${
|
|
212250
|
+
options.viteConfig.logger.info(`${prefix2} ${import_picocolors11.green("switched to")} ${import_picocolors11.green(import_picocolors11.bold(newMode))} ${import_picocolors11.green("mode")}`);
|
|
211806
212251
|
}
|
|
211807
212252
|
var toggleModeHotkey = (options) => ({
|
|
211808
212253
|
key: "m",
|
|
211809
|
-
description: "toggle platform/standalone mode
|
|
212254
|
+
description: `${import_picocolors11.cyan(import_picocolors11.bold("[playcademy]"))} toggle platform/standalone mode`,
|
|
211810
212255
|
action: () => toggleMode(options)
|
|
211811
212256
|
});
|
|
211812
212257
|
|
|
@@ -211815,6 +212260,7 @@ function getHotkeys(options) {
|
|
|
211815
212260
|
return [
|
|
211816
212261
|
toggleModeHotkey(options),
|
|
211817
212262
|
recreateDatabaseHotkey(options),
|
|
212263
|
+
cyclePlatformRoleHotkey(options),
|
|
211818
212264
|
cycleTimebackRoleHotkey(options)
|
|
211819
212265
|
];
|
|
211820
212266
|
}
|
|
@@ -211847,11 +212293,12 @@ async function configureServerHook(server, context) {
|
|
|
211847
212293
|
setupProcessShutdownHandlers();
|
|
211848
212294
|
if (hasActiveServers()) {
|
|
211849
212295
|
await cleanupServers();
|
|
211850
|
-
await new Promise((resolve2) => setTimeout(resolve2, 100));
|
|
211851
212296
|
}
|
|
211852
|
-
const
|
|
212297
|
+
const backendPort = context.backendPort ?? DEFAULT_PORTS3.BACKEND;
|
|
212298
|
+
const sandboxPort = context.sandboxPort ?? DEFAULT_PORTS3.SANDBOX;
|
|
211853
212299
|
const platformModeOptions = {
|
|
211854
212300
|
startSandbox: context.options.startSandbox,
|
|
212301
|
+
sandboxPort,
|
|
211855
212302
|
verbose: context.options.verbose,
|
|
211856
212303
|
logLevel: context.options.logLevel,
|
|
211857
212304
|
sandboxUrl: context.options.sandboxUrl,
|
|
@@ -211861,18 +212308,19 @@ async function configureServerHook(server, context) {
|
|
|
211861
212308
|
databasePath: context.options.databasePath,
|
|
211862
212309
|
realtimeEnabled: context.options.realtimeEnabled,
|
|
211863
212310
|
realtimePort: context.options.realtimePort,
|
|
211864
|
-
|
|
211865
|
-
|
|
212311
|
+
hideBadge: context.options.hideBadge,
|
|
212312
|
+
backendPort,
|
|
211866
212313
|
configPath: context.options.configPath,
|
|
211867
212314
|
timeback: context.options.timeback
|
|
211868
212315
|
};
|
|
211869
212316
|
if (context.options.mode === "standalone") {
|
|
211870
212317
|
await configureStandaloneMode(server, context.viteConfig, {
|
|
211871
|
-
|
|
212318
|
+
backendPort,
|
|
211872
212319
|
configPath: context.options.configPath
|
|
211873
212320
|
});
|
|
211874
212321
|
} else {
|
|
211875
212322
|
await configurePlatformMode(server, context.viteConfig, platformModeOptions);
|
|
212323
|
+
setupConfigWatcher(server, context.viteConfig, platformModeOptions);
|
|
211876
212324
|
}
|
|
211877
212325
|
const originalBindCLIShortcuts = server.bindCLIShortcuts?.bind(server);
|
|
211878
212326
|
if (originalBindCLIShortcuts) {
|
|
@@ -211892,12 +212340,12 @@ async function configureServerHook(server, context) {
|
|
|
211892
212340
|
}
|
|
211893
212341
|
|
|
211894
212342
|
// src/hooks/write-bundle.ts
|
|
211895
|
-
import
|
|
212343
|
+
import path8 from "node:path";
|
|
211896
212344
|
async function writeBundleHook(context) {
|
|
211897
212345
|
if (!context.viteConfig) {
|
|
211898
212346
|
throw new Error("[Playcademy] Vite config not resolved before writeBundle");
|
|
211899
212347
|
}
|
|
211900
|
-
const outDir = context.viteConfig.build.outDir ||
|
|
212348
|
+
const outDir = context.viteConfig.build.outDir || path8.join(process.cwd(), "dist");
|
|
211901
212349
|
try {
|
|
211902
212350
|
await generatePlaycademyManifest(context.viteConfig, outDir, context.buildOutputs);
|
|
211903
212351
|
if (context.options.autoZip) {
|
|
@@ -211916,7 +212364,7 @@ async function writeBundleHook(context) {
|
|
|
211916
212364
|
function resolveOptions(options) {
|
|
211917
212365
|
const exportOptions = options.export ?? {};
|
|
211918
212366
|
const sandboxOptions = options.sandbox ?? {};
|
|
211919
|
-
const
|
|
212367
|
+
const displayOptions = options.display ?? {};
|
|
211920
212368
|
const realtimeOptions = sandboxOptions.realtime ?? {};
|
|
211921
212369
|
return {
|
|
211922
212370
|
configPath: options.configPath,
|
|
@@ -211932,7 +212380,7 @@ function resolveOptions(options) {
|
|
|
211932
212380
|
databasePath: sandboxOptions.databasePath,
|
|
211933
212381
|
realtimeEnabled: realtimeOptions.enabled ?? false,
|
|
211934
212382
|
realtimePort: realtimeOptions.port,
|
|
211935
|
-
|
|
212383
|
+
hideBadge: displayOptions.hideBadge ?? false,
|
|
211936
212384
|
timeback: options.timeback
|
|
211937
212385
|
};
|
|
211938
212386
|
}
|
|
@@ -211941,6 +212389,7 @@ function createPluginContext(options) {
|
|
|
211941
212389
|
options: resolveOptions(options),
|
|
211942
212390
|
viteConfig: null,
|
|
211943
212391
|
backendPort: null,
|
|
212392
|
+
sandboxPort: null,
|
|
211944
212393
|
buildOutputs: {}
|
|
211945
212394
|
};
|
|
211946
212395
|
}
|