playcademy 0.13.1 → 0.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/constants.d.ts +0 -2
- package/dist/constants.js +0 -2
- package/dist/db.js +0 -2
- package/dist/index.js +228 -155
- package/dist/templates/playcademy-gitignore.template +0 -1
- package/dist/utils.js +106 -62
- package/package.json +2 -2
package/dist/constants.d.ts
CHANGED
|
@@ -72,8 +72,6 @@ declare const CLI_FILES: {
|
|
|
72
72
|
readonly AUTH_STORE: "auth.json";
|
|
73
73
|
/** Games deployment info store */
|
|
74
74
|
readonly GAMES_STORE: "games.json";
|
|
75
|
-
/** Dev server PID file */
|
|
76
|
-
readonly DEV_SERVER_PID: "dev-server.pid";
|
|
77
75
|
/** Initial database file (before miniflare) */
|
|
78
76
|
readonly INITIAL_DATABASE: "initial.sqlite";
|
|
79
77
|
};
|
package/dist/constants.js
CHANGED
|
@@ -35,8 +35,6 @@ var CLI_FILES = {
|
|
|
35
35
|
AUTH_STORE: "auth.json",
|
|
36
36
|
/** Games deployment info store */
|
|
37
37
|
GAMES_STORE: "games.json",
|
|
38
|
-
/** Dev server PID file */
|
|
39
|
-
DEV_SERVER_PID: "dev-server.pid",
|
|
40
38
|
/** Initial database file (before miniflare) */
|
|
41
39
|
INITIAL_DATABASE: "initial.sqlite"
|
|
42
40
|
};
|
package/dist/db.js
CHANGED
|
@@ -39,8 +39,6 @@ var CLI_FILES = {
|
|
|
39
39
|
AUTH_STORE: "auth.json",
|
|
40
40
|
/** Games deployment info store */
|
|
41
41
|
GAMES_STORE: "games.json",
|
|
42
|
-
/** Dev server PID file */
|
|
43
|
-
DEV_SERVER_PID: "dev-server.pid",
|
|
44
42
|
/** Initial database file (before miniflare) */
|
|
45
43
|
INITIAL_DATABASE: "initial.sqlite"
|
|
46
44
|
};
|
package/dist/index.js
CHANGED
|
@@ -2145,8 +2145,6 @@ var init_paths = __esm({
|
|
|
2145
2145
|
AUTH_STORE: "auth.json",
|
|
2146
2146
|
/** Games deployment info store */
|
|
2147
2147
|
GAMES_STORE: "games.json",
|
|
2148
|
-
/** Dev server PID file */
|
|
2149
|
-
DEV_SERVER_PID: "dev-server.pid",
|
|
2150
2148
|
/** Initial database file (before miniflare) */
|
|
2151
2149
|
INITIAL_DATABASE: "initial.sqlite"
|
|
2152
2150
|
};
|
|
@@ -3726,7 +3724,7 @@ import { program } from "commander";
|
|
|
3726
3724
|
|
|
3727
3725
|
// src/commands/init/index.ts
|
|
3728
3726
|
import { execSync as execSync3 } from "child_process";
|
|
3729
|
-
import { readFileSync as readFileSync5, writeFileSync as
|
|
3727
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync6 } from "fs";
|
|
3730
3728
|
import { resolve as resolve10 } from "path";
|
|
3731
3729
|
|
|
3732
3730
|
// ../../node_modules/@inquirer/core/dist/esm/lib/errors.js
|
|
@@ -5441,7 +5439,6 @@ import { checkbox, confirm, input, select } from "@inquirer/prompts";
|
|
|
5441
5439
|
import { bold as bold3, cyan as cyan2 } from "colorette";
|
|
5442
5440
|
|
|
5443
5441
|
// src/lib/init/scaffold.ts
|
|
5444
|
-
init_src();
|
|
5445
5442
|
init_constants2();
|
|
5446
5443
|
init_core();
|
|
5447
5444
|
init_loader2();
|
|
@@ -5454,21 +5451,15 @@ var playcademyGitignoreTemplate = loadTemplateString("playcademy-gitignore");
|
|
|
5454
5451
|
async function scaffoldApiDirectory(apiDirectory, sampleRoutes) {
|
|
5455
5452
|
const apiPath = resolve6(getWorkspace(), apiDirectory);
|
|
5456
5453
|
const samplePath = join9(apiPath, "sample");
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
for (const route of sampleRoutes) {
|
|
5467
|
-
writeFileSync3(join9(samplePath, route.filename), route.template, "utf-8");
|
|
5468
|
-
}
|
|
5469
|
-
},
|
|
5470
|
-
"API directory scaffolded"
|
|
5471
|
-
);
|
|
5454
|
+
if (!existsSync8(apiPath)) {
|
|
5455
|
+
mkdirSync3(apiPath, { recursive: true });
|
|
5456
|
+
}
|
|
5457
|
+
if (!existsSync8(samplePath)) {
|
|
5458
|
+
mkdirSync3(samplePath, { recursive: true });
|
|
5459
|
+
}
|
|
5460
|
+
for (const route of sampleRoutes) {
|
|
5461
|
+
writeFileSync3(join9(samplePath, route.filename), route.template, "utf-8");
|
|
5462
|
+
}
|
|
5472
5463
|
}
|
|
5473
5464
|
function validateApiDirectoryDoesNotExist(value) {
|
|
5474
5465
|
const dirPath = resolve6(getWorkspace(), value.trim());
|
|
@@ -5486,7 +5477,8 @@ function ensurePlaycademyGitignore() {
|
|
|
5486
5477
|
const gitignorePath = join9(playcademyDir, ".gitignore");
|
|
5487
5478
|
writeFileSync3(gitignorePath, playcademyGitignoreTemplate);
|
|
5488
5479
|
}
|
|
5489
|
-
async function scaffoldIntegrations(
|
|
5480
|
+
async function scaffoldIntegrations(gameName, options) {
|
|
5481
|
+
const { customRoutes, database, kv } = options;
|
|
5490
5482
|
ensurePlaycademyGitignore();
|
|
5491
5483
|
if (customRoutes) {
|
|
5492
5484
|
const sampleRoutes = [
|
|
@@ -7570,58 +7562,10 @@ function displayRegisteredRoutes(integrations, customRoutes = []) {
|
|
|
7570
7562
|
}
|
|
7571
7563
|
}
|
|
7572
7564
|
|
|
7573
|
-
// src/lib/dev/pid.ts
|
|
7574
|
-
init_constants2();
|
|
7575
|
-
init_core();
|
|
7576
|
-
import { existsSync as existsSync15, readFileSync as readFileSync4, unlinkSync as unlinkSync2 } from "fs";
|
|
7577
|
-
import { mkdir as mkdir4, unlink, writeFile as writeFile4 } from "fs/promises";
|
|
7578
|
-
import { join as join15 } from "path";
|
|
7579
|
-
function getDevServerPidPath() {
|
|
7580
|
-
return join15(getWorkspace(), CLI_DIRECTORIES.WORKSPACE, CLI_FILES.DEV_SERVER_PID);
|
|
7581
|
-
}
|
|
7582
|
-
async function createDevServerPidFile() {
|
|
7583
|
-
const pidPath = getDevServerPidFile();
|
|
7584
|
-
const pidDir = join15(getWorkspace(), CLI_DIRECTORIES.WORKSPACE);
|
|
7585
|
-
await mkdir4(pidDir, { recursive: true });
|
|
7586
|
-
await writeFile4(pidPath, process.pid.toString());
|
|
7587
|
-
}
|
|
7588
|
-
async function removeDevServerPidFile() {
|
|
7589
|
-
const pidPath = getDevServerPidFile();
|
|
7590
|
-
if (existsSync15(pidPath)) {
|
|
7591
|
-
await unlink(pidPath);
|
|
7592
|
-
}
|
|
7593
|
-
}
|
|
7594
|
-
function isDevServerRunning() {
|
|
7595
|
-
const pidPath = getDevServerPidFile();
|
|
7596
|
-
if (!existsSync15(pidPath)) {
|
|
7597
|
-
return false;
|
|
7598
|
-
}
|
|
7599
|
-
try {
|
|
7600
|
-
const pid = parseInt(readFileSync4(pidPath, "utf8").trim(), 10);
|
|
7601
|
-
if (process.platform === "win32") {
|
|
7602
|
-
return true;
|
|
7603
|
-
} else {
|
|
7604
|
-
try {
|
|
7605
|
-
process.kill(pid, 0);
|
|
7606
|
-
return true;
|
|
7607
|
-
} catch {
|
|
7608
|
-
unlinkSync2(pidPath);
|
|
7609
|
-
return false;
|
|
7610
|
-
}
|
|
7611
|
-
}
|
|
7612
|
-
} catch {
|
|
7613
|
-
unlinkSync2(pidPath);
|
|
7614
|
-
return false;
|
|
7615
|
-
}
|
|
7616
|
-
}
|
|
7617
|
-
function getDevServerPidFile() {
|
|
7618
|
-
return getDevServerPidPath();
|
|
7619
|
-
}
|
|
7620
|
-
|
|
7621
7565
|
// src/lib/dev/reload.ts
|
|
7622
7566
|
init_constants2();
|
|
7623
7567
|
init_core();
|
|
7624
|
-
import { join as
|
|
7568
|
+
import { join as join15, relative as relative3 } from "path";
|
|
7625
7569
|
import chokidar from "chokidar";
|
|
7626
7570
|
import { bold as bold4, cyan as cyan3, dim as dim6, green as green3 } from "colorette";
|
|
7627
7571
|
function formatTime() {
|
|
@@ -7638,9 +7582,9 @@ function startHotReload(onReload, options = {}) {
|
|
|
7638
7582
|
const customRoutesConfig = options.config?.integrations?.customRoutes;
|
|
7639
7583
|
const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
|
|
7640
7584
|
const watchPaths = [
|
|
7641
|
-
|
|
7642
|
-
|
|
7643
|
-
|
|
7585
|
+
join15(workspace, customRoutesDir),
|
|
7586
|
+
join15(workspace, "playcademy.config.js"),
|
|
7587
|
+
join15(workspace, "playcademy.config.json")
|
|
7644
7588
|
];
|
|
7645
7589
|
const watcher = chokidar.watch(watchPaths, {
|
|
7646
7590
|
persistent: true,
|
|
@@ -7681,18 +7625,134 @@ function startHotReload(onReload, options = {}) {
|
|
|
7681
7625
|
|
|
7682
7626
|
// src/lib/dev/server.ts
|
|
7683
7627
|
init_src2();
|
|
7628
|
+
import { mkdir as mkdir4 } from "fs/promises";
|
|
7629
|
+
import { join as join17 } from "path";
|
|
7630
|
+
import { Miniflare } from "miniflare";
|
|
7631
|
+
|
|
7632
|
+
// ../utils/src/port.ts
|
|
7633
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
7634
|
+
import { createServer as createServer2 } from "node:net";
|
|
7635
|
+
import { homedir as homedir3 } from "node:os";
|
|
7636
|
+
import { join as join16 } from "node:path";
|
|
7637
|
+
async function isPortAvailableOnHost(port, host) {
|
|
7638
|
+
return new Promise((resolve11) => {
|
|
7639
|
+
const server = createServer2();
|
|
7640
|
+
server.once("error", () => {
|
|
7641
|
+
resolve11(false);
|
|
7642
|
+
});
|
|
7643
|
+
server.once("listening", () => {
|
|
7644
|
+
server.close();
|
|
7645
|
+
resolve11(true);
|
|
7646
|
+
});
|
|
7647
|
+
server.listen(port, host);
|
|
7648
|
+
});
|
|
7649
|
+
}
|
|
7650
|
+
async function findAvailablePort(startPort = 4321) {
|
|
7651
|
+
const allInterfacesAvailable = await isPortAvailableOnHost(startPort, "0.0.0.0");
|
|
7652
|
+
const ipv6Available = await isPortAvailableOnHost(startPort, "::1");
|
|
7653
|
+
if (allInterfacesAvailable && ipv6Available) {
|
|
7654
|
+
return startPort;
|
|
7655
|
+
}
|
|
7656
|
+
return findAvailablePort(startPort + 1);
|
|
7657
|
+
}
|
|
7658
|
+
function getRegistryPath() {
|
|
7659
|
+
const home = homedir3();
|
|
7660
|
+
const dir = join16(home, ".playcademy");
|
|
7661
|
+
if (!existsSync15(dir)) {
|
|
7662
|
+
mkdirSync4(dir, { recursive: true });
|
|
7663
|
+
}
|
|
7664
|
+
return join16(dir, ".proc");
|
|
7665
|
+
}
|
|
7666
|
+
function readRegistry() {
|
|
7667
|
+
const registryPath = getRegistryPath();
|
|
7668
|
+
if (!existsSync15(registryPath)) {
|
|
7669
|
+
return {};
|
|
7670
|
+
}
|
|
7671
|
+
try {
|
|
7672
|
+
const content = readFileSync4(registryPath, "utf-8");
|
|
7673
|
+
return JSON.parse(content);
|
|
7674
|
+
} catch {
|
|
7675
|
+
return {};
|
|
7676
|
+
}
|
|
7677
|
+
}
|
|
7678
|
+
function writeRegistry(registry) {
|
|
7679
|
+
const registryPath = getRegistryPath();
|
|
7680
|
+
writeFileSync4(registryPath, JSON.stringify(registry, null, 2), "utf-8");
|
|
7681
|
+
}
|
|
7682
|
+
function getServerKey(type, port) {
|
|
7683
|
+
return `${type}-${port}`;
|
|
7684
|
+
}
|
|
7685
|
+
function writeServerInfo(type, info) {
|
|
7686
|
+
const registry = readRegistry();
|
|
7687
|
+
const key = getServerKey(type, info.port);
|
|
7688
|
+
registry[key] = info;
|
|
7689
|
+
writeRegistry(registry);
|
|
7690
|
+
}
|
|
7691
|
+
function readServerInfo(type, projectRoot) {
|
|
7692
|
+
const registry = readRegistry();
|
|
7693
|
+
const servers = Object.entries(registry).filter(([key]) => key.startsWith(`${type}-`)).map(([, info]) => info);
|
|
7694
|
+
if (servers.length === 0) {
|
|
7695
|
+
return null;
|
|
7696
|
+
}
|
|
7697
|
+
if (projectRoot) {
|
|
7698
|
+
const match = servers.find((s) => s.projectRoot === projectRoot);
|
|
7699
|
+
return match || null;
|
|
7700
|
+
}
|
|
7701
|
+
return servers[0] || null;
|
|
7702
|
+
}
|
|
7703
|
+
function isServerRunning(type, projectRoot) {
|
|
7704
|
+
const info = readServerInfo(type, projectRoot);
|
|
7705
|
+
if (!info) {
|
|
7706
|
+
return false;
|
|
7707
|
+
}
|
|
7708
|
+
try {
|
|
7709
|
+
if (process.platform === "win32") {
|
|
7710
|
+
return true;
|
|
7711
|
+
} else {
|
|
7712
|
+
process.kill(info.pid, 0);
|
|
7713
|
+
return true;
|
|
7714
|
+
}
|
|
7715
|
+
} catch {
|
|
7716
|
+
cleanupServerInfo(type, projectRoot);
|
|
7717
|
+
return false;
|
|
7718
|
+
}
|
|
7719
|
+
}
|
|
7720
|
+
function cleanupServerInfo(type, projectRoot, pid) {
|
|
7721
|
+
const registry = readRegistry();
|
|
7722
|
+
const keysToRemove = [];
|
|
7723
|
+
for (const [key, info] of Object.entries(registry)) {
|
|
7724
|
+
if (key.startsWith(`${type}-`)) {
|
|
7725
|
+
let matches = true;
|
|
7726
|
+
if (projectRoot && info.projectRoot !== projectRoot) {
|
|
7727
|
+
matches = false;
|
|
7728
|
+
}
|
|
7729
|
+
if (pid !== void 0 && info.pid !== pid) {
|
|
7730
|
+
matches = false;
|
|
7731
|
+
}
|
|
7732
|
+
if (matches) {
|
|
7733
|
+
keysToRemove.push(key);
|
|
7734
|
+
}
|
|
7735
|
+
}
|
|
7736
|
+
}
|
|
7737
|
+
for (const key of keysToRemove) {
|
|
7738
|
+
delete registry[key];
|
|
7739
|
+
}
|
|
7740
|
+
if (keysToRemove.length > 0) {
|
|
7741
|
+
writeRegistry(registry);
|
|
7742
|
+
}
|
|
7743
|
+
}
|
|
7744
|
+
|
|
7745
|
+
// src/lib/dev/server.ts
|
|
7684
7746
|
init_constants2();
|
|
7685
7747
|
init_loader();
|
|
7686
7748
|
init_core();
|
|
7687
|
-
import { mkdir as mkdir5 } from "fs/promises";
|
|
7688
|
-
import { join as join17 } from "path";
|
|
7689
|
-
import { Miniflare } from "miniflare";
|
|
7690
7749
|
async function startDevServer(options) {
|
|
7691
7750
|
const {
|
|
7692
|
-
port,
|
|
7751
|
+
port: preferredPort,
|
|
7693
7752
|
config: providedConfig,
|
|
7694
7753
|
platformUrl = process.env.PLAYCADEMY_BASE_URL || "http://localhost:5174"
|
|
7695
7754
|
} = options;
|
|
7755
|
+
const port = await findAvailablePort(preferredPort);
|
|
7696
7756
|
const config = providedConfig ?? await loadConfig();
|
|
7697
7757
|
const hasSandboxTimebackCreds = !!process.env.TIMEBACK_API_CLIENT_ID;
|
|
7698
7758
|
const devConfig = config.integrations?.timeback && !hasSandboxTimebackCreds ? { ...config, integrations: { ...config.integrations, timeback: void 0 } } : config;
|
|
@@ -7727,12 +7787,13 @@ async function startDevServer(options) {
|
|
|
7727
7787
|
if (hasDatabase) {
|
|
7728
7788
|
await initializeDatabase(mf);
|
|
7729
7789
|
}
|
|
7790
|
+
await writeBackendServerInfo(port);
|
|
7730
7791
|
return mf;
|
|
7731
7792
|
}
|
|
7732
7793
|
async function ensureDatabaseDirectory() {
|
|
7733
7794
|
const dbDir = join17(getWorkspace(), CLI_DIRECTORIES.DATABASE);
|
|
7734
7795
|
try {
|
|
7735
|
-
await
|
|
7796
|
+
await mkdir4(dbDir, { recursive: true });
|
|
7736
7797
|
} catch (error) {
|
|
7737
7798
|
throw new Error(`Failed to create database directory: ${getErrorMessage(error)}`);
|
|
7738
7799
|
}
|
|
@@ -7741,7 +7802,7 @@ async function ensureDatabaseDirectory() {
|
|
|
7741
7802
|
async function ensureKvDirectory() {
|
|
7742
7803
|
const kvDir = join17(getWorkspace(), CLI_DIRECTORIES.KV);
|
|
7743
7804
|
try {
|
|
7744
|
-
await
|
|
7805
|
+
await mkdir4(kvDir, { recursive: true });
|
|
7745
7806
|
} catch (error) {
|
|
7746
7807
|
throw new Error(`Failed to create KV directory: ${getErrorMessage(error)}`);
|
|
7747
7808
|
}
|
|
@@ -7750,7 +7811,15 @@ async function ensureKvDirectory() {
|
|
|
7750
7811
|
async function initializeDatabase(mf) {
|
|
7751
7812
|
const d1 = await mf.getD1Database("DB");
|
|
7752
7813
|
await d1.exec("SELECT 1");
|
|
7753
|
-
|
|
7814
|
+
}
|
|
7815
|
+
async function writeBackendServerInfo(port) {
|
|
7816
|
+
writeServerInfo("backend", {
|
|
7817
|
+
pid: process.pid,
|
|
7818
|
+
port,
|
|
7819
|
+
url: `http://localhost:${port}/api`,
|
|
7820
|
+
startedAt: Date.now(),
|
|
7821
|
+
projectRoot: getWorkspace()
|
|
7822
|
+
});
|
|
7754
7823
|
}
|
|
7755
7824
|
|
|
7756
7825
|
// src/lib/timeback/cleanup.ts
|
|
@@ -7816,7 +7885,7 @@ function displayResourcesStatus(resources, logger2) {
|
|
|
7816
7885
|
}
|
|
7817
7886
|
|
|
7818
7887
|
// src/commands/init/config.ts
|
|
7819
|
-
import { writeFileSync as
|
|
7888
|
+
import { writeFileSync as writeFileSync5 } from "fs";
|
|
7820
7889
|
import { resolve as resolve9 } from "path";
|
|
7821
7890
|
init_file_loader();
|
|
7822
7891
|
init_constants2();
|
|
@@ -7868,7 +7937,7 @@ var configCommand = new Command("config").description("Create playcademy.config
|
|
|
7868
7937
|
emoji: gameInfo.emoji,
|
|
7869
7938
|
timeback: timebackConfig ?? void 0
|
|
7870
7939
|
});
|
|
7871
|
-
|
|
7940
|
+
writeFileSync5(resolve9(getWorkspace(), configFileName), configContent, "utf-8");
|
|
7872
7941
|
displayConfigSuccess(!!timebackConfig);
|
|
7873
7942
|
} catch (error) {
|
|
7874
7943
|
logger.newLine();
|
|
@@ -7934,27 +8003,35 @@ var initCommand = new Command2("init").description("Initialize a playcademy.conf
|
|
|
7934
8003
|
kv,
|
|
7935
8004
|
customRoutes
|
|
7936
8005
|
} = await promptForIntegrations();
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
customRoutes
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
|
|
7954
|
-
|
|
7955
|
-
|
|
7956
|
-
|
|
7957
|
-
|
|
8006
|
+
logger.newLine();
|
|
8007
|
+
await runStep(
|
|
8008
|
+
"Setting up project",
|
|
8009
|
+
async () => {
|
|
8010
|
+
let depsAdded = false;
|
|
8011
|
+
const hasPackageJsonFile = hasPackageJson();
|
|
8012
|
+
if (hasPackageJsonFile) {
|
|
8013
|
+
const sdkAdded = await addPlaycademySdk();
|
|
8014
|
+
if (sdkAdded) depsAdded = true;
|
|
8015
|
+
}
|
|
8016
|
+
if (customRoutes || database) {
|
|
8017
|
+
const scaffoldOptions = { customRoutes, database, kv };
|
|
8018
|
+
const scaffoldDepsAdded = await scaffoldIntegrations(
|
|
8019
|
+
gameInfo.name,
|
|
8020
|
+
scaffoldOptions
|
|
8021
|
+
);
|
|
8022
|
+
if (scaffoldDepsAdded) depsAdded = true;
|
|
8023
|
+
}
|
|
8024
|
+
if (depsAdded) {
|
|
8025
|
+
const pm = detectPackageManager(getWorkspace());
|
|
8026
|
+
const installCmd = getInstallCommand(pm);
|
|
8027
|
+
execSync3(installCmd, {
|
|
8028
|
+
cwd: getWorkspace(),
|
|
8029
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
8030
|
+
});
|
|
8031
|
+
}
|
|
8032
|
+
},
|
|
8033
|
+
"Project configured"
|
|
8034
|
+
);
|
|
7958
8035
|
logger.newLine();
|
|
7959
8036
|
const configContent = configFormat === "js" ? generateJsConfig({
|
|
7960
8037
|
name: gameInfo.name,
|
|
@@ -7973,7 +8050,7 @@ var initCommand = new Command2("init").description("Initialize a playcademy.conf
|
|
|
7973
8050
|
kv: kv ?? void 0,
|
|
7974
8051
|
timeback: timebackConfig ?? void 0
|
|
7975
8052
|
});
|
|
7976
|
-
|
|
8053
|
+
writeFileSync6(resolve10(getWorkspace(), configFileName), configContent, "utf-8");
|
|
7977
8054
|
displaySuccessMessage({
|
|
7978
8055
|
configFileName,
|
|
7979
8056
|
apiDirectory: customRoutes?.directory ?? null,
|
|
@@ -7995,20 +8072,9 @@ async function addPlaycademySdk() {
|
|
|
7995
8072
|
}
|
|
7996
8073
|
if (!pkg.dependencies) pkg.dependencies = {};
|
|
7997
8074
|
pkg.dependencies["@playcademy/sdk"] = "latest";
|
|
7998
|
-
|
|
8075
|
+
writeFileSync6(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
7999
8076
|
return true;
|
|
8000
8077
|
}
|
|
8001
|
-
async function installDependencies() {
|
|
8002
|
-
const pm = detectPackageManager(getWorkspace());
|
|
8003
|
-
const installCmd = getInstallCommand(pm);
|
|
8004
|
-
await runStep(
|
|
8005
|
-
"Installing dependencies...",
|
|
8006
|
-
async () => {
|
|
8007
|
-
execSync3(installCmd, { cwd: getWorkspace(), stdio: ["ignore", "ignore", "ignore"] });
|
|
8008
|
-
},
|
|
8009
|
-
"Dependencies installed"
|
|
8010
|
-
);
|
|
8011
|
-
}
|
|
8012
8078
|
initCommand.addCommand(configCommand);
|
|
8013
8079
|
|
|
8014
8080
|
// src/commands/login.ts
|
|
@@ -8596,7 +8662,43 @@ var getStatusCommand = new Command11("status").description("Check your developer
|
|
|
8596
8662
|
|
|
8597
8663
|
// src/commands/dev/server.ts
|
|
8598
8664
|
import { blueBright as blueBright4, underline as underline2 } from "colorette";
|
|
8665
|
+
function setupCleanupHandlers(workspace, getServer) {
|
|
8666
|
+
let isShuttingDown = false;
|
|
8667
|
+
const cleanup = () => {
|
|
8668
|
+
if (isShuttingDown) return;
|
|
8669
|
+
isShuttingDown = true;
|
|
8670
|
+
cleanupServerInfo("backend", workspace, process.pid);
|
|
8671
|
+
const server = getServer();
|
|
8672
|
+
if (server) {
|
|
8673
|
+
server.dispose().then(() => process.exit(0)).catch(() => process.exit(1));
|
|
8674
|
+
} else {
|
|
8675
|
+
process.exit(0);
|
|
8676
|
+
}
|
|
8677
|
+
};
|
|
8678
|
+
process.on("SIGINT", cleanup);
|
|
8679
|
+
process.on("SIGTERM", cleanup);
|
|
8680
|
+
}
|
|
8681
|
+
async function setupServerHotReload(serverRef, port, workspace, config, loggerEnabled) {
|
|
8682
|
+
return startHotReload(
|
|
8683
|
+
async () => {
|
|
8684
|
+
if (serverRef.current) {
|
|
8685
|
+
await serverRef.current.dispose();
|
|
8686
|
+
}
|
|
8687
|
+
const newConfig = await loadConfig();
|
|
8688
|
+
serverRef.current = await startDevServer({
|
|
8689
|
+
port,
|
|
8690
|
+
config: newConfig,
|
|
8691
|
+
logger: loggerEnabled
|
|
8692
|
+
});
|
|
8693
|
+
await discoverRoutes(getCustomRoutesDirectory(workspace, newConfig));
|
|
8694
|
+
},
|
|
8695
|
+
{ config }
|
|
8696
|
+
);
|
|
8697
|
+
}
|
|
8599
8698
|
async function runDevServer(options) {
|
|
8699
|
+
const workspace = getWorkspace();
|
|
8700
|
+
const serverRef = { current: null };
|
|
8701
|
+
setupCleanupHandlers(workspace, () => serverRef.current);
|
|
8600
8702
|
try {
|
|
8601
8703
|
logger.newLine();
|
|
8602
8704
|
const config = await loadConfig();
|
|
@@ -8614,52 +8716,27 @@ async function runDevServer(options) {
|
|
|
8614
8716
|
return;
|
|
8615
8717
|
}
|
|
8616
8718
|
const port = parseInt(options.port, 10);
|
|
8617
|
-
|
|
8719
|
+
const server = await startDevServer({
|
|
8618
8720
|
port,
|
|
8619
8721
|
config,
|
|
8620
8722
|
logger: options.logger !== false
|
|
8621
8723
|
});
|
|
8622
|
-
|
|
8724
|
+
serverRef.current = server;
|
|
8623
8725
|
logger.success(`Game API started: ${blueBright4(underline2(`http://localhost:${port}/api`))}`);
|
|
8624
8726
|
logger.newLine();
|
|
8625
|
-
const workspace = getWorkspace();
|
|
8626
8727
|
const customRoutesDir = getCustomRoutesDirectory(workspace, config);
|
|
8627
|
-
|
|
8728
|
+
const customRoutes = await discoverRoutes(customRoutesDir);
|
|
8628
8729
|
displayRegisteredRoutes(config.integrations, customRoutes);
|
|
8629
8730
|
logger.newLine();
|
|
8630
|
-
if (
|
|
8631
|
-
|
|
8632
|
-
async () => {
|
|
8633
|
-
await server.dispose();
|
|
8634
|
-
clearModuleCache();
|
|
8635
|
-
const newConfig = await loadConfig();
|
|
8636
|
-
server = await startDevServer({
|
|
8637
|
-
port,
|
|
8638
|
-
config: newConfig,
|
|
8639
|
-
logger: options.logger !== false
|
|
8640
|
-
});
|
|
8641
|
-
const newCustomRoutesDir = getCustomRoutesDirectory(workspace, newConfig);
|
|
8642
|
-
customRoutes = await discoverRoutes(newCustomRoutesDir);
|
|
8643
|
-
},
|
|
8644
|
-
{ config }
|
|
8645
|
-
);
|
|
8731
|
+
if (options.reload !== false) {
|
|
8732
|
+
await setupServerHotReload(serverRef, port, workspace, config, options.logger !== false);
|
|
8646
8733
|
}
|
|
8647
8734
|
logger.remark(`Press ${underline2("ctrl+c")} to stop`);
|
|
8648
8735
|
logger.newLine();
|
|
8649
|
-
process.on("SIGINT", async () => {
|
|
8650
|
-
await removeDevServerPidFile();
|
|
8651
|
-
process.exit(0);
|
|
8652
|
-
});
|
|
8653
|
-
process.on("SIGTERM", async () => {
|
|
8654
|
-
await removeDevServerPidFile();
|
|
8655
|
-
process.exit(0);
|
|
8656
|
-
});
|
|
8657
8736
|
} catch (error) {
|
|
8658
8737
|
logAndExit(error, logger, { prefix: "Failed to start dev server" });
|
|
8659
8738
|
}
|
|
8660
8739
|
}
|
|
8661
|
-
function clearModuleCache() {
|
|
8662
|
-
}
|
|
8663
8740
|
|
|
8664
8741
|
// src/commands/dev/index.ts
|
|
8665
8742
|
var devCommand = new Command12("dev").description("Start local backend development server").option("-p, --port <port>", "Backend server port", String(DEFAULT_PORTS.BACKEND)).option("--no-reload", "Disable hot reload").option("--no-logger", "Disable HTTP request logging").action(runDevServer);
|
|
@@ -8771,7 +8848,7 @@ async function runDbDiff() {
|
|
|
8771
8848
|
// src/commands/db/init.ts
|
|
8772
8849
|
init_file_loader();
|
|
8773
8850
|
init_constants2();
|
|
8774
|
-
import { readFileSync as readFileSync6, writeFileSync as
|
|
8851
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync7 } from "fs";
|
|
8775
8852
|
import { input as input6 } from "@inquirer/prompts";
|
|
8776
8853
|
async function runDbInit() {
|
|
8777
8854
|
try {
|
|
@@ -8828,7 +8905,7 @@ async function runDbInit() {
|
|
|
8828
8905
|
);
|
|
8829
8906
|
}
|
|
8830
8907
|
}
|
|
8831
|
-
|
|
8908
|
+
writeFileSync7(configFile.path, updatedContent, "utf-8");
|
|
8832
8909
|
logger.success(`Updated ${configFile.path.split("/").pop()}`);
|
|
8833
8910
|
}
|
|
8834
8911
|
logger.newLine();
|
|
@@ -8850,12 +8927,12 @@ async function runDbInit() {
|
|
|
8850
8927
|
// src/commands/db/reset.ts
|
|
8851
8928
|
init_src2();
|
|
8852
8929
|
init_src();
|
|
8853
|
-
init_constants2();
|
|
8854
8930
|
import { spawn } from "child_process";
|
|
8855
8931
|
import { existsSync as existsSync16, rmSync as rmSync2 } from "fs";
|
|
8856
8932
|
import { join as join18 } from "path";
|
|
8857
8933
|
import { confirm as confirm6 } from "@inquirer/prompts";
|
|
8858
8934
|
import { Miniflare as Miniflare2 } from "miniflare";
|
|
8935
|
+
init_constants2();
|
|
8859
8936
|
async function runDbReset() {
|
|
8860
8937
|
try {
|
|
8861
8938
|
const workspace = getWorkspace();
|
|
@@ -8868,7 +8945,7 @@ async function runDbReset() {
|
|
|
8868
8945
|
return;
|
|
8869
8946
|
}
|
|
8870
8947
|
logger.newLine();
|
|
8871
|
-
if (
|
|
8948
|
+
if (isServerRunning("backend", workspace)) {
|
|
8872
8949
|
logger.admonition("warning", "Stop Dev Server First", [
|
|
8873
8950
|
"The development server must be stopped before resetting the database.",
|
|
8874
8951
|
"Stop the server (Ctrl+C) and run this command again to ensure a clean reset."
|
|
@@ -10590,7 +10667,7 @@ import { Command as Command27 } from "commander";
|
|
|
10590
10667
|
// src/commands/debug/bundle.ts
|
|
10591
10668
|
init_src();
|
|
10592
10669
|
init_constants2();
|
|
10593
|
-
import { writeFileSync as
|
|
10670
|
+
import { writeFileSync as writeFileSync8 } from "fs";
|
|
10594
10671
|
import { join as join28 } from "path";
|
|
10595
10672
|
import { Command as Command26 } from "commander";
|
|
10596
10673
|
var bundleCommand = new Command26("bundle").description("Bundle and inspect the game backend worker code (for debugging)").option("-o, --output <path>", "Output file path", CLI_DEFAULT_OUTPUTS.WORKER_BUNDLE).option("--minify", "Minify the output").option("--sourcemap", "Include source maps").action(async (options) => {
|
|
@@ -10622,7 +10699,7 @@ var bundleCommand = new Command26("bundle").description("Bundle and inspect the
|
|
|
10622
10699
|
(result) => `Bundled ${formatSize(result.code.length)}`
|
|
10623
10700
|
);
|
|
10624
10701
|
const outputPath = join28(workspace, options.output);
|
|
10625
|
-
|
|
10702
|
+
writeFileSync8(outputPath, bundle.code, "utf-8");
|
|
10626
10703
|
logger.success(`Bundle saved to ${options.output}`);
|
|
10627
10704
|
logger.newLine();
|
|
10628
10705
|
logger.highlight("Bundle Analysis");
|
|
@@ -10700,7 +10777,6 @@ export {
|
|
|
10700
10777
|
compareIntegrationKeys,
|
|
10701
10778
|
confirmDeploymentPlan,
|
|
10702
10779
|
createClient,
|
|
10703
|
-
createDevServerPidFile,
|
|
10704
10780
|
deployBackendIfNeeded,
|
|
10705
10781
|
deployExternalGame,
|
|
10706
10782
|
deployGameBackend,
|
|
@@ -10739,7 +10815,6 @@ export {
|
|
|
10739
10815
|
getCustomRoutesSize,
|
|
10740
10816
|
getDeployedGame,
|
|
10741
10817
|
getDeploymentId,
|
|
10742
|
-
getDevServerPidPath,
|
|
10743
10818
|
getDrizzleKitApiExports,
|
|
10744
10819
|
getEnvironment,
|
|
10745
10820
|
getErrorMessage,
|
|
@@ -10773,7 +10848,6 @@ export {
|
|
|
10773
10848
|
importTypescriptDefault,
|
|
10774
10849
|
importTypescriptFile,
|
|
10775
10850
|
integrationChangeDetectors,
|
|
10776
|
-
isDevServerRunning,
|
|
10777
10851
|
listProfiles,
|
|
10778
10852
|
loadAuthStore,
|
|
10779
10853
|
loadConfig,
|
|
@@ -10791,7 +10865,6 @@ export {
|
|
|
10791
10865
|
promptForTimeBackIntegration,
|
|
10792
10866
|
registerCustomRoutes,
|
|
10793
10867
|
removeDeployedGame,
|
|
10794
|
-
removeDevServerPidFile,
|
|
10795
10868
|
removeProfile,
|
|
10796
10869
|
reportCancellation,
|
|
10797
10870
|
reportDeploymentSuccess,
|
package/dist/utils.js
CHANGED
|
@@ -303,16 +303,6 @@ var CLI_DIRECTORIES = {
|
|
|
303
303
|
/** KV storage directory within workspace */
|
|
304
304
|
KV: join(WORKSPACE_NAME, "kv")
|
|
305
305
|
};
|
|
306
|
-
var CLI_FILES = {
|
|
307
|
-
/** Auth store file in user config directory */
|
|
308
|
-
AUTH_STORE: "auth.json",
|
|
309
|
-
/** Games deployment info store */
|
|
310
|
-
GAMES_STORE: "games.json",
|
|
311
|
-
/** Dev server PID file */
|
|
312
|
-
DEV_SERVER_PID: "dev-server.pid",
|
|
313
|
-
/** Initial database file (before miniflare) */
|
|
314
|
-
INITIAL_DATABASE: "initial.sqlite"
|
|
315
|
-
};
|
|
316
306
|
|
|
317
307
|
// src/constants/timeback.ts
|
|
318
308
|
var CONFIG_FILE_NAMES = [
|
|
@@ -567,10 +557,70 @@ function processConfigVariables(config) {
|
|
|
567
557
|
}
|
|
568
558
|
|
|
569
559
|
// src/lib/dev/server.ts
|
|
570
|
-
import { mkdir as
|
|
560
|
+
import { mkdir as mkdir2 } from "fs/promises";
|
|
571
561
|
import { join as join7 } from "path";
|
|
572
562
|
import { Miniflare } from "miniflare";
|
|
573
563
|
|
|
564
|
+
// ../utils/src/port.ts
|
|
565
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
566
|
+
import { createServer } from "node:net";
|
|
567
|
+
import { homedir } from "node:os";
|
|
568
|
+
import { join as join2 } from "node:path";
|
|
569
|
+
async function isPortAvailableOnHost(port, host) {
|
|
570
|
+
return new Promise((resolve4) => {
|
|
571
|
+
const server = createServer();
|
|
572
|
+
server.once("error", () => {
|
|
573
|
+
resolve4(false);
|
|
574
|
+
});
|
|
575
|
+
server.once("listening", () => {
|
|
576
|
+
server.close();
|
|
577
|
+
resolve4(true);
|
|
578
|
+
});
|
|
579
|
+
server.listen(port, host);
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
async function findAvailablePort(startPort = 4321) {
|
|
583
|
+
const allInterfacesAvailable = await isPortAvailableOnHost(startPort, "0.0.0.0");
|
|
584
|
+
const ipv6Available = await isPortAvailableOnHost(startPort, "::1");
|
|
585
|
+
if (allInterfacesAvailable && ipv6Available) {
|
|
586
|
+
return startPort;
|
|
587
|
+
}
|
|
588
|
+
return findAvailablePort(startPort + 1);
|
|
589
|
+
}
|
|
590
|
+
function getRegistryPath() {
|
|
591
|
+
const home = homedir();
|
|
592
|
+
const dir = join2(home, ".playcademy");
|
|
593
|
+
if (!existsSync2(dir)) {
|
|
594
|
+
mkdirSync(dir, { recursive: true });
|
|
595
|
+
}
|
|
596
|
+
return join2(dir, ".proc");
|
|
597
|
+
}
|
|
598
|
+
function readRegistry() {
|
|
599
|
+
const registryPath = getRegistryPath();
|
|
600
|
+
if (!existsSync2(registryPath)) {
|
|
601
|
+
return {};
|
|
602
|
+
}
|
|
603
|
+
try {
|
|
604
|
+
const content = readFileSync(registryPath, "utf-8");
|
|
605
|
+
return JSON.parse(content);
|
|
606
|
+
} catch {
|
|
607
|
+
return {};
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
function writeRegistry(registry) {
|
|
611
|
+
const registryPath = getRegistryPath();
|
|
612
|
+
writeFileSync(registryPath, JSON.stringify(registry, null, 2), "utf-8");
|
|
613
|
+
}
|
|
614
|
+
function getServerKey(type, port) {
|
|
615
|
+
return `${type}-${port}`;
|
|
616
|
+
}
|
|
617
|
+
function writeServerInfo(type, info) {
|
|
618
|
+
const registry = readRegistry();
|
|
619
|
+
const key = getServerKey(type, info.port);
|
|
620
|
+
registry[key] = info;
|
|
621
|
+
writeRegistry(registry);
|
|
622
|
+
}
|
|
623
|
+
|
|
574
624
|
// src/lib/core/client.ts
|
|
575
625
|
import { PlaycademyClient } from "@playcademy/sdk";
|
|
576
626
|
|
|
@@ -816,7 +866,7 @@ var CROSS_MARK = String.fromCodePoint(10006);
|
|
|
816
866
|
init_package_json();
|
|
817
867
|
|
|
818
868
|
// src/lib/templates/loader.ts
|
|
819
|
-
import { existsSync as
|
|
869
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
820
870
|
import { dirname as dirname3, resolve as resolve3 } from "path";
|
|
821
871
|
import { fileURLToPath } from "url";
|
|
822
872
|
var currentDir = dirname3(fileURLToPath(import.meta.url));
|
|
@@ -829,8 +879,8 @@ function loadTemplateString(filename) {
|
|
|
829
879
|
resolve3(currentDir, "templates", name)
|
|
830
880
|
]);
|
|
831
881
|
for (const candidate of candidatePaths) {
|
|
832
|
-
if (
|
|
833
|
-
return
|
|
882
|
+
if (existsSync3(candidate)) {
|
|
883
|
+
return readFileSync2(candidate, "utf-8");
|
|
834
884
|
}
|
|
835
885
|
}
|
|
836
886
|
throw new Error(`Template not found: ${filename}. Searched: ${candidatePaths.join(", ")}`);
|
|
@@ -839,12 +889,12 @@ function loadTemplateString(filename) {
|
|
|
839
889
|
// src/lib/core/import.ts
|
|
840
890
|
import { mkdtempSync, rmSync } from "fs";
|
|
841
891
|
import { tmpdir } from "os";
|
|
842
|
-
import { join as
|
|
892
|
+
import { join as join3 } from "path";
|
|
843
893
|
import { pathToFileURL } from "url";
|
|
844
894
|
import * as esbuild from "esbuild";
|
|
845
895
|
async function importTypescriptFile(filePath, bundleOptions) {
|
|
846
|
-
const tempDir = mkdtempSync(
|
|
847
|
-
const outFile =
|
|
896
|
+
const tempDir = mkdtempSync(join3(tmpdir(), "playcademy-import-"));
|
|
897
|
+
const outFile = join3(tempDir, "bundle.mjs");
|
|
848
898
|
try {
|
|
849
899
|
await esbuild.build({
|
|
850
900
|
entryPoints: [filePath],
|
|
@@ -872,8 +922,8 @@ async function importTypescriptDefault(filePath, bundleOptions) {
|
|
|
872
922
|
}
|
|
873
923
|
|
|
874
924
|
// src/lib/deploy/bundle.ts
|
|
875
|
-
import { existsSync as
|
|
876
|
-
import { join as
|
|
925
|
+
import { existsSync as existsSync4 } from "fs";
|
|
926
|
+
import { join as join5 } from "path";
|
|
877
927
|
|
|
878
928
|
// ../edge-play/src/entry.ts
|
|
879
929
|
var entry_default = "/**\n * Game Backend Entry Point\n *\n * This file is the main entry point for deployed game backends.\n * It creates a Hono app and registers all enabled integration routes.\n *\n * Bundled with esbuild and deployed to Cloudflare Workers (or AWS Lambda).\n * Config is injected at build time via esbuild's `define` option.\n */\n\nimport { Hono } from 'hono'\nimport { cors } from 'hono/cors'\n\nimport { PlaycademyClient } from '@playcademy/sdk/server'\n\nimport { ENV_VARS } from './constants'\nimport { registerBuiltinRoutes } from './register-routes'\n\nimport type { PlaycademyConfig } from '@playcademy/sdk/server'\nimport type { HonoEnv } from './types'\n\n/**\n * Config injected at build time by esbuild\n *\n * The `declare const` tells TypeScript \"this exists at runtime, trust me.\"\n * During bundling, esbuild's `define` option does literal text replacement:\n *\n * Example bundling:\n * Source: if (PLAYCADEMY_CONFIG.integrations.timeback) { ... }\n * Define: { 'PLAYCADEMY_CONFIG': JSON.stringify({ integrations: { timeback: {...} } }) }\n * Output: if ({\"integrations\":{\"timeback\":{...}}}.integrations.timeback) { ... }\n *\n * This enables tree-shaking: if timeback is not configured, those code paths are removed.\n * The bundled Worker only includes the routes that are actually enabled.\n */\ndeclare const PLAYCADEMY_CONFIG: PlaycademyConfig & {\n customRoutes?: Array<{ path: string; file: string }>\n}\n\n// XXX: Polyfill process global for SDK compatibility\n// SDK code may reference process.env without importing it\n// @ts-expect-error - Adding global for Worker environment\nglobalThis.process = {\n env: {}, // Populated per-request from Worker env bindings\n cwd: () => '/',\n}\n\nconst app = new Hono<HonoEnv>()\n\n// TODO: Harden CORS in production - restrict to trusted origins:\n// - Game's assetBundleBase (for hosted games)\n// - Game's externalUrl (for external games)\n// - Platform frontend domains (hub.playcademy.com, hub.dev.playcademy.net)\n// This would require passing game metadata through env bindings during deployment\napp.use(\n '*',\n cors({\n origin: '*', // Permissive for now\n allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],\n allowHeaders: ['Content-Type', 'Authorization'],\n }),\n)\n\nlet sdkPromise: Promise<PlaycademyClient> | null = null\n\napp.use('*', async (c, next) => {\n // Populate process.env from Worker bindings for SDK compatibility\n globalThis.process.env = {\n [ENV_VARS.PLAYCADEMY_API_KEY]: c.env.PLAYCADEMY_API_KEY,\n [ENV_VARS.GAME_ID]: c.env.GAME_ID,\n [ENV_VARS.PLAYCADEMY_BASE_URL]: c.env.PLAYCADEMY_BASE_URL,\n }\n\n // Set config for all routes\n c.set('config', PLAYCADEMY_CONFIG)\n c.set('customRoutes', PLAYCADEMY_CONFIG.customRoutes || [])\n\n await next()\n})\n\n// Initialize SDK lazily on first request\napp.use('*', async (c, next) => {\n if (!sdkPromise) {\n sdkPromise = PlaycademyClient.init({\n apiKey: c.env[ENV_VARS.PLAYCADEMY_API_KEY],\n gameId: c.env[ENV_VARS.GAME_ID],\n baseUrl: c.env[ENV_VARS.PLAYCADEMY_BASE_URL],\n config: PLAYCADEMY_CONFIG,\n })\n }\n\n c.set('sdk', await sdkPromise)\n await next()\n})\n\n/**\n * Register built-in integration routes based on enabled integrations\n *\n * This function conditionally imports and registers routes like:\n * - POST /api/integrations/timeback/end-activity (if timeback enabled)\n * - GET /api/health (always included)\n *\n * Uses dynamic imports for tree-shaking: if an integration is not enabled,\n * its route code is completely removed from the bundle.\n */\nawait registerBuiltinRoutes(app, PLAYCADEMY_CONFIG.integrations)\n\nexport default app\n";
|
|
@@ -1129,7 +1179,7 @@ function textLoaderPlugin() {
|
|
|
1129
1179
|
init_file_loader();
|
|
1130
1180
|
import { mkdir, writeFile } from "fs/promises";
|
|
1131
1181
|
import { tmpdir as tmpdir2 } from "os";
|
|
1132
|
-
import { join as
|
|
1182
|
+
import { join as join4, relative } from "path";
|
|
1133
1183
|
|
|
1134
1184
|
// src/lib/deploy/hash.ts
|
|
1135
1185
|
import { createHash } from "crypto";
|
|
@@ -1148,7 +1198,7 @@ async function discoverRoutes(apiDir) {
|
|
|
1148
1198
|
const routes = await Promise.all(
|
|
1149
1199
|
files.map(async (file) => {
|
|
1150
1200
|
const routePath = filePathToRoutePath(file);
|
|
1151
|
-
const absolutePath =
|
|
1201
|
+
const absolutePath = join4(apiDir, file);
|
|
1152
1202
|
const relativePath = relative(getWorkspace(), absolutePath);
|
|
1153
1203
|
const methods = await detectExportedMethods(absolutePath);
|
|
1154
1204
|
return {
|
|
@@ -1208,10 +1258,10 @@ async function transpileRoute(filePath) {
|
|
|
1208
1258
|
if (!result.outputFiles?.[0]) {
|
|
1209
1259
|
throw new Error("Transpilation failed: no output");
|
|
1210
1260
|
}
|
|
1211
|
-
const tempDir =
|
|
1261
|
+
const tempDir = join4(tmpdir2(), "playcademy-dev");
|
|
1212
1262
|
await mkdir(tempDir, { recursive: true });
|
|
1213
1263
|
const hash = hashContent(filePath).slice(0, 12);
|
|
1214
|
-
const jsPath =
|
|
1264
|
+
const jsPath = join4(tempDir, `${hash}.mjs`);
|
|
1215
1265
|
await writeFile(jsPath, result.outputFiles[0].text);
|
|
1216
1266
|
return jsPath;
|
|
1217
1267
|
}
|
|
@@ -1222,7 +1272,7 @@ async function discoverCustomRoutes(config) {
|
|
|
1222
1272
|
const workspace = getWorkspace();
|
|
1223
1273
|
const customRoutesConfig = config.integrations?.customRoutes;
|
|
1224
1274
|
const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
|
|
1225
|
-
const customRoutes = await discoverRoutes(
|
|
1275
|
+
const customRoutes = await discoverRoutes(join5(workspace, customRoutesDir));
|
|
1226
1276
|
const customRouteData = customRoutes.map((r) => ({
|
|
1227
1277
|
path: r.path,
|
|
1228
1278
|
file: r.file,
|
|
@@ -1234,15 +1284,15 @@ async function discoverCustomRoutes(config) {
|
|
|
1234
1284
|
function resolveEmbeddedSourcePaths() {
|
|
1235
1285
|
const workspace = getWorkspace();
|
|
1236
1286
|
const distDir = new URL(".", import.meta.url).pathname;
|
|
1237
|
-
const embeddedEdgeSrc =
|
|
1238
|
-
const isBuiltPackage =
|
|
1287
|
+
const embeddedEdgeSrc = join5(distDir, "edge-play", "src");
|
|
1288
|
+
const isBuiltPackage = existsSync4(embeddedEdgeSrc);
|
|
1239
1289
|
const monorepoRoot = getMonorepoRoot();
|
|
1240
|
-
const monorepoEdgeSrc =
|
|
1290
|
+
const monorepoEdgeSrc = join5(monorepoRoot, "packages/edge-play/src");
|
|
1241
1291
|
const edgePlaySrc = isBuiltPackage ? embeddedEdgeSrc : monorepoEdgeSrc;
|
|
1242
|
-
const cliPackageRoot = isBuiltPackage ?
|
|
1243
|
-
const cliNodeModules = isBuiltPackage ?
|
|
1244
|
-
const workspaceNodeModules =
|
|
1245
|
-
const constantsEntry = isBuiltPackage ?
|
|
1292
|
+
const cliPackageRoot = isBuiltPackage ? join5(distDir, "../../..") : join5(monorepoRoot, "packages/cli");
|
|
1293
|
+
const cliNodeModules = isBuiltPackage ? join5(cliPackageRoot, "node_modules") : monorepoRoot;
|
|
1294
|
+
const workspaceNodeModules = join5(workspace, "node_modules");
|
|
1295
|
+
const constantsEntry = isBuiltPackage ? join5(embeddedEdgeSrc, "..", "..", "constants", "src", "index.ts") : join5(monorepoRoot, "packages", "constants", "src", "index.ts");
|
|
1246
1296
|
return {
|
|
1247
1297
|
isBuiltPackage,
|
|
1248
1298
|
edgePlaySrc,
|
|
@@ -1302,16 +1352,16 @@ function createEsbuildConfig(entryCode, paths, bundleConfig, customRoutesDir, op
|
|
|
1302
1352
|
// │ Example: import * as route from '@game-api/hello.ts' │
|
|
1303
1353
|
// │ Resolves to: /user-project/server/api/hello.ts │
|
|
1304
1354
|
// └─────────────────────────────────────────────────────────────────┘
|
|
1305
|
-
"@game-api":
|
|
1355
|
+
"@game-api": join5(workspace, customRoutesDir),
|
|
1306
1356
|
// ┌─ Node.js polyfills for Cloudflare Workers ──────────────────────┐
|
|
1307
1357
|
// │ Workers don't have fs, path, os, etc. Redirect to polyfills │
|
|
1308
1358
|
// │ that throw helpful errors if user code tries to use them. │
|
|
1309
1359
|
// └─────────────────────────────────────────────────────────────────┘
|
|
1310
|
-
fs:
|
|
1311
|
-
"fs/promises":
|
|
1312
|
-
path:
|
|
1313
|
-
os:
|
|
1314
|
-
process:
|
|
1360
|
+
fs: join5(edgePlaySrc, "polyfills.js"),
|
|
1361
|
+
"fs/promises": join5(edgePlaySrc, "polyfills.js"),
|
|
1362
|
+
path: join5(edgePlaySrc, "polyfills.js"),
|
|
1363
|
+
os: join5(edgePlaySrc, "polyfills.js"),
|
|
1364
|
+
process: join5(edgePlaySrc, "polyfills.js")
|
|
1315
1365
|
},
|
|
1316
1366
|
// ──── Build Plugins ────
|
|
1317
1367
|
plugins: [textLoaderPlugin()],
|
|
@@ -1378,8 +1428,8 @@ import { checkbox, confirm, input, select } from "@inquirer/prompts";
|
|
|
1378
1428
|
import { bold as bold3, cyan as cyan2 } from "colorette";
|
|
1379
1429
|
|
|
1380
1430
|
// src/lib/init/database.ts
|
|
1381
|
-
import { existsSync as
|
|
1382
|
-
import { join as
|
|
1431
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1432
|
+
import { join as join6 } from "path";
|
|
1383
1433
|
var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
|
|
1384
1434
|
var dbSchemaUsersTemplate = loadTemplateString("database/db-schema-users.ts");
|
|
1385
1435
|
var dbSchemaScoresTemplate = loadTemplateString("database/db-schema-scores.ts");
|
|
@@ -1390,9 +1440,9 @@ var packageTemplate = loadTemplateString("database/package.json");
|
|
|
1390
1440
|
var rootGitignoreTemplate = loadTemplateString("gitignore");
|
|
1391
1441
|
function hasDatabaseSetup() {
|
|
1392
1442
|
const workspace = getWorkspace();
|
|
1393
|
-
const drizzleConfigPath =
|
|
1394
|
-
const drizzleConfigJsPath =
|
|
1395
|
-
return
|
|
1443
|
+
const drizzleConfigPath = join6(workspace, "drizzle.config.ts");
|
|
1444
|
+
const drizzleConfigJsPath = join6(workspace, "drizzle.config.js");
|
|
1445
|
+
return existsSync5(drizzleConfigPath) || existsSync5(drizzleConfigJsPath);
|
|
1396
1446
|
}
|
|
1397
1447
|
|
|
1398
1448
|
// src/lib/init/scaffold.ts
|
|
@@ -1409,29 +1459,14 @@ function hasKVSetup(config) {
|
|
|
1409
1459
|
return !!config.integrations?.kv;
|
|
1410
1460
|
}
|
|
1411
1461
|
|
|
1412
|
-
// src/lib/dev/pid.ts
|
|
1413
|
-
import { mkdir as mkdir2, unlink, writeFile as writeFile2 } from "fs/promises";
|
|
1414
|
-
import { join as join6 } from "path";
|
|
1415
|
-
function getDevServerPidPath() {
|
|
1416
|
-
return join6(getWorkspace(), CLI_DIRECTORIES.WORKSPACE, CLI_FILES.DEV_SERVER_PID);
|
|
1417
|
-
}
|
|
1418
|
-
async function createDevServerPidFile() {
|
|
1419
|
-
const pidPath = getDevServerPidFile();
|
|
1420
|
-
const pidDir = join6(getWorkspace(), CLI_DIRECTORIES.WORKSPACE);
|
|
1421
|
-
await mkdir2(pidDir, { recursive: true });
|
|
1422
|
-
await writeFile2(pidPath, process.pid.toString());
|
|
1423
|
-
}
|
|
1424
|
-
function getDevServerPidFile() {
|
|
1425
|
-
return getDevServerPidPath();
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
1462
|
// src/lib/dev/server.ts
|
|
1429
1463
|
async function startDevServer(options) {
|
|
1430
1464
|
const {
|
|
1431
|
-
port,
|
|
1465
|
+
port: preferredPort,
|
|
1432
1466
|
config: providedConfig,
|
|
1433
1467
|
platformUrl = process.env.PLAYCADEMY_BASE_URL || "http://localhost:5174"
|
|
1434
1468
|
} = options;
|
|
1469
|
+
const port = await findAvailablePort(preferredPort);
|
|
1435
1470
|
const config = providedConfig ?? await loadConfig();
|
|
1436
1471
|
const hasSandboxTimebackCreds = !!process.env.TIMEBACK_API_CLIENT_ID;
|
|
1437
1472
|
const devConfig = config.integrations?.timeback && !hasSandboxTimebackCreds ? { ...config, integrations: { ...config.integrations, timeback: void 0 } } : config;
|
|
@@ -1466,12 +1501,13 @@ async function startDevServer(options) {
|
|
|
1466
1501
|
if (hasDatabase) {
|
|
1467
1502
|
await initializeDatabase(mf);
|
|
1468
1503
|
}
|
|
1504
|
+
await writeBackendServerInfo(port);
|
|
1469
1505
|
return mf;
|
|
1470
1506
|
}
|
|
1471
1507
|
async function ensureDatabaseDirectory() {
|
|
1472
1508
|
const dbDir = join7(getWorkspace(), CLI_DIRECTORIES.DATABASE);
|
|
1473
1509
|
try {
|
|
1474
|
-
await
|
|
1510
|
+
await mkdir2(dbDir, { recursive: true });
|
|
1475
1511
|
} catch (error) {
|
|
1476
1512
|
throw new Error(`Failed to create database directory: ${getErrorMessage(error)}`);
|
|
1477
1513
|
}
|
|
@@ -1480,7 +1516,7 @@ async function ensureDatabaseDirectory() {
|
|
|
1480
1516
|
async function ensureKvDirectory() {
|
|
1481
1517
|
const kvDir = join7(getWorkspace(), CLI_DIRECTORIES.KV);
|
|
1482
1518
|
try {
|
|
1483
|
-
await
|
|
1519
|
+
await mkdir2(kvDir, { recursive: true });
|
|
1484
1520
|
} catch (error) {
|
|
1485
1521
|
throw new Error(`Failed to create KV directory: ${getErrorMessage(error)}`);
|
|
1486
1522
|
}
|
|
@@ -1489,7 +1525,15 @@ async function ensureKvDirectory() {
|
|
|
1489
1525
|
async function initializeDatabase(mf) {
|
|
1490
1526
|
const d1 = await mf.getD1Database("DB");
|
|
1491
1527
|
await d1.exec("SELECT 1");
|
|
1492
|
-
|
|
1528
|
+
}
|
|
1529
|
+
async function writeBackendServerInfo(port) {
|
|
1530
|
+
writeServerInfo("backend", {
|
|
1531
|
+
pid: process.pid,
|
|
1532
|
+
port,
|
|
1533
|
+
url: `http://localhost:${port}/api`,
|
|
1534
|
+
startedAt: Date.now(),
|
|
1535
|
+
projectRoot: getWorkspace()
|
|
1536
|
+
});
|
|
1493
1537
|
}
|
|
1494
1538
|
|
|
1495
1539
|
// src/lib/dev/reload.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "playcademy",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"module": "./dist/index.js",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@inquirer/prompts": "^7.8.6",
|
|
43
|
-
"@playcademy/sdk": "0.1.
|
|
43
|
+
"@playcademy/sdk": "0.1.6",
|
|
44
44
|
"better-sqlite3": "^12.4.1",
|
|
45
45
|
"chokidar": "^4.0.3",
|
|
46
46
|
"colorette": "^2.0.20",
|