playcademy 0.11.14 → 0.12.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/dist/constants.d.ts +72 -1
- package/dist/constants.js +46 -0
- package/dist/db.d.ts +61 -0
- package/dist/db.js +671 -0
- package/dist/edge-play/src/constants.ts +3 -28
- package/dist/{types.d.ts → index.d.ts} +118 -14
- package/dist/index.js +4650 -6635
- package/dist/templates/api/sample-route-with-db.ts.template +141 -0
- package/dist/templates/config/playcademy.config.js.template +4 -0
- package/dist/templates/config/playcademy.config.json.template +3 -0
- package/dist/templates/config/timeback-config.js.template +8 -0
- package/dist/templates/database/db-index.ts.template +21 -0
- package/dist/templates/database/db-schema-index.ts.template +8 -0
- package/dist/templates/database/db-schema-scores.ts.template +43 -0
- package/dist/templates/database/db-schema-users.ts.template +23 -0
- package/dist/templates/database/db-seed.ts.template +52 -0
- package/dist/templates/database/db-types.ts.template +21 -0
- package/dist/templates/database/drizzle-config.ts.template +13 -0
- package/dist/templates/database/package.json.template +20 -0
- package/dist/templates/gitignore.template +17 -0
- package/dist/templates/playcademy-gitignore.template +3 -0
- package/dist/utils.d.ts +31 -14
- package/dist/utils.js +523 -490
- package/package.json +19 -3
- package/dist/templates/backend-config.js.template +0 -6
- package/dist/templates/playcademy.config.js.template +0 -4
- package/dist/templates/playcademy.config.json.template +0 -3
- package/dist/templates/timeback-config.js.template +0 -17
- /package/dist/templates/{sample-route.ts → api/sample-route.ts.template} +0 -0
- /package/dist/templates/{integrations-config.js.template → config/integrations-config.js.template} +0 -0
package/dist/db.js
ADDED
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
+
var __esm = (fn, res) => function __init() {
|
|
3
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
// ../utils/src/package-json.ts
|
|
7
|
+
var init_package_json = __esm({
|
|
8
|
+
"../utils/src/package-json.ts"() {
|
|
9
|
+
"use strict";
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// ../utils/src/file-loader.ts
|
|
14
|
+
var init_file_loader = __esm({
|
|
15
|
+
"../utils/src/file-loader.ts"() {
|
|
16
|
+
"use strict";
|
|
17
|
+
init_package_json();
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// src/lib/db/path.ts
|
|
22
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, unlinkSync } from "fs";
|
|
23
|
+
import { join } from "path";
|
|
24
|
+
import Database from "better-sqlite3";
|
|
25
|
+
|
|
26
|
+
// src/constants/paths.ts
|
|
27
|
+
var CLI_DIRECTORIES = {
|
|
28
|
+
/** Root directory for CLI artifacts in workspace */
|
|
29
|
+
WORKSPACE: ".playcademy",
|
|
30
|
+
/** Database directory within workspace */
|
|
31
|
+
DATABASE: ".playcademy/db"
|
|
32
|
+
};
|
|
33
|
+
var CLI_FILES = {
|
|
34
|
+
/** Auth store file in user config directory */
|
|
35
|
+
AUTH_STORE: "auth.json",
|
|
36
|
+
/** Games deployment info store */
|
|
37
|
+
GAMES_STORE: "games.json",
|
|
38
|
+
/** Dev server PID file */
|
|
39
|
+
DEV_SERVER_PID: "dev-server.pid",
|
|
40
|
+
/** Initial database file (before miniflare) */
|
|
41
|
+
INITIAL_DATABASE: "initial.sqlite"
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// ../constants/src/overworld.ts
|
|
45
|
+
var ITEM_SLUGS = {
|
|
46
|
+
/** Primary platform currency */
|
|
47
|
+
PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
|
|
48
|
+
/** Experience points currency */
|
|
49
|
+
PLAYCADEMY_XP: "PLAYCADEMY_XP",
|
|
50
|
+
/** Core platform badges */
|
|
51
|
+
FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
|
|
52
|
+
EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
|
|
53
|
+
FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
|
|
54
|
+
/** Example items */
|
|
55
|
+
COMMON_SWORD: "COMMON_SWORD",
|
|
56
|
+
SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
|
|
57
|
+
SMALL_BACKPACK: "SMALL_BACKPACK",
|
|
58
|
+
/** Placeable items */
|
|
59
|
+
LAVA_LAMP: "LAVA_LAMP",
|
|
60
|
+
BOOMBOX: "BOOMBOX",
|
|
61
|
+
CABIN_BED: "CABIN_BED"
|
|
62
|
+
};
|
|
63
|
+
var CURRENCIES = {
|
|
64
|
+
/** Primary platform currency slug */
|
|
65
|
+
PRIMARY: ITEM_SLUGS.PLAYCADEMY_CREDITS,
|
|
66
|
+
/** Experience points slug */
|
|
67
|
+
XP: ITEM_SLUGS.PLAYCADEMY_XP
|
|
68
|
+
};
|
|
69
|
+
var BADGES = {
|
|
70
|
+
FOUNDING_MEMBER: ITEM_SLUGS.FOUNDING_MEMBER_BADGE,
|
|
71
|
+
EARLY_ADOPTER: ITEM_SLUGS.EARLY_ADOPTER_BADGE,
|
|
72
|
+
FIRST_GAME: ITEM_SLUGS.FIRST_GAME_BADGE
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// src/lib/db/path.ts
|
|
76
|
+
var DB_DIRECTORY = CLI_DIRECTORIES.DATABASE;
|
|
77
|
+
var INITIAL_DB_NAME = CLI_FILES.INITIAL_DATABASE;
|
|
78
|
+
var ensureDirectoryExists = (dir) => {
|
|
79
|
+
if (!existsSync(dir)) {
|
|
80
|
+
mkdirSync(dir, { recursive: true });
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var createEmptyDatabase = (path) => {
|
|
84
|
+
const db = new Database(path);
|
|
85
|
+
db.close();
|
|
86
|
+
};
|
|
87
|
+
var findMiniflareDatabase = (dbDir) => {
|
|
88
|
+
const miniflareDir = join(dbDir, "miniflare-D1DatabaseObject");
|
|
89
|
+
if (!existsSync(miniflareDir)) return null;
|
|
90
|
+
const sqliteFiles = readdirSync(miniflareDir).filter((file) => file.endsWith(".sqlite"));
|
|
91
|
+
if (sqliteFiles.length === 0) return null;
|
|
92
|
+
return join(miniflareDir, sqliteFiles[0]);
|
|
93
|
+
};
|
|
94
|
+
var migrateInitialDbToTarget = (initialPath, targetPath) => {
|
|
95
|
+
if (!existsSync(initialPath)) return;
|
|
96
|
+
copyFileSync(initialPath, targetPath);
|
|
97
|
+
unlinkSync(initialPath);
|
|
98
|
+
};
|
|
99
|
+
function getDevDbPath() {
|
|
100
|
+
const initialDbPath = join(DB_DIRECTORY, INITIAL_DB_NAME);
|
|
101
|
+
ensureDirectoryExists(DB_DIRECTORY);
|
|
102
|
+
const miniflareDbPath = findMiniflareDatabase(DB_DIRECTORY);
|
|
103
|
+
if (miniflareDbPath) {
|
|
104
|
+
migrateInitialDbToTarget(initialDbPath, miniflareDbPath);
|
|
105
|
+
return miniflareDbPath;
|
|
106
|
+
}
|
|
107
|
+
if (!existsSync(initialDbPath)) {
|
|
108
|
+
createEmptyDatabase(initialDbPath);
|
|
109
|
+
}
|
|
110
|
+
return initialDbPath;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/lib/db/reset.ts
|
|
114
|
+
import { execSync as execSync2 } from "child_process";
|
|
115
|
+
import Database2 from "better-sqlite3";
|
|
116
|
+
|
|
117
|
+
// ../utils/src/ansi.ts
|
|
118
|
+
var colors = {
|
|
119
|
+
black: "\x1B[30m",
|
|
120
|
+
red: "\x1B[31m",
|
|
121
|
+
green: "\x1B[32m",
|
|
122
|
+
yellow: "\x1B[33m",
|
|
123
|
+
blue: "\x1B[34m",
|
|
124
|
+
magenta: "\x1B[35m",
|
|
125
|
+
cyan: "\x1B[36m",
|
|
126
|
+
white: "\x1B[37m",
|
|
127
|
+
gray: "\x1B[90m"
|
|
128
|
+
};
|
|
129
|
+
var styles = {
|
|
130
|
+
reset: "\x1B[0m",
|
|
131
|
+
bold: "\x1B[1m",
|
|
132
|
+
dim: "\x1B[2m",
|
|
133
|
+
italic: "\x1B[3m",
|
|
134
|
+
underline: "\x1B[4m"
|
|
135
|
+
};
|
|
136
|
+
var cursor = {
|
|
137
|
+
hide: "\x1B[?25l",
|
|
138
|
+
show: "\x1B[?25h",
|
|
139
|
+
up: (n) => `\x1B[${n}A`,
|
|
140
|
+
down: (n) => `\x1B[${n}B`,
|
|
141
|
+
forward: (n) => `\x1B[${n}C`,
|
|
142
|
+
back: (n) => `\x1B[${n}D`,
|
|
143
|
+
clearLine: "\x1B[K",
|
|
144
|
+
clearScreen: "\x1B[2J",
|
|
145
|
+
home: "\x1B[H"
|
|
146
|
+
};
|
|
147
|
+
function color(text, colorCode) {
|
|
148
|
+
return `${colorCode}${text}${styles.reset}`;
|
|
149
|
+
}
|
|
150
|
+
function dim(text) {
|
|
151
|
+
return color(text, styles.dim);
|
|
152
|
+
}
|
|
153
|
+
function bold(text) {
|
|
154
|
+
return color(text, styles.bold);
|
|
155
|
+
}
|
|
156
|
+
var isInteractive = typeof process !== "undefined" && process.stdout?.isTTY && !process.env.CI && process.env.TERM !== "dumb";
|
|
157
|
+
function stripAnsi(text) {
|
|
158
|
+
return text.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, "");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ../utils/src/spinner.ts
|
|
162
|
+
import { stdout } from "process";
|
|
163
|
+
var SPINNER_FRAMES = [
|
|
164
|
+
10251,
|
|
165
|
+
10265,
|
|
166
|
+
10297,
|
|
167
|
+
10296,
|
|
168
|
+
10300,
|
|
169
|
+
10292,
|
|
170
|
+
10278,
|
|
171
|
+
10279,
|
|
172
|
+
10247,
|
|
173
|
+
10255
|
|
174
|
+
].map((code) => String.fromCodePoint(code));
|
|
175
|
+
var CHECK_MARK = String.fromCodePoint(10004);
|
|
176
|
+
var CROSS_MARK = String.fromCodePoint(10006);
|
|
177
|
+
var SPINNER_INTERVAL = 80;
|
|
178
|
+
var Spinner = class {
|
|
179
|
+
tasks = /* @__PURE__ */ new Map();
|
|
180
|
+
frameIndex = 0;
|
|
181
|
+
intervalId = null;
|
|
182
|
+
renderCount = 0;
|
|
183
|
+
previousLineCount = 0;
|
|
184
|
+
printedTasks = /* @__PURE__ */ new Set();
|
|
185
|
+
indent;
|
|
186
|
+
constructor(taskIds, texts, options) {
|
|
187
|
+
this.indent = options?.indent ?? 0;
|
|
188
|
+
taskIds.forEach((id, index) => {
|
|
189
|
+
this.tasks.set(id, {
|
|
190
|
+
text: texts[index] || "",
|
|
191
|
+
status: "pending"
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
start() {
|
|
196
|
+
if (isInteractive) {
|
|
197
|
+
stdout.write(cursor.hide);
|
|
198
|
+
this.render();
|
|
199
|
+
this.intervalId = setInterval(() => {
|
|
200
|
+
this.frameIndex = (this.frameIndex + 1) % SPINNER_FRAMES.length;
|
|
201
|
+
this.render();
|
|
202
|
+
}, SPINNER_INTERVAL);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
updateTask(taskId, status, finalText) {
|
|
206
|
+
const task = this.tasks.get(taskId);
|
|
207
|
+
if (task) {
|
|
208
|
+
task.status = status;
|
|
209
|
+
if (finalText) task.finalText = finalText;
|
|
210
|
+
if (!isInteractive) {
|
|
211
|
+
this.renderNonInteractive(taskId, task);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
renderNonInteractive(taskId, task) {
|
|
216
|
+
const key = `${taskId}-${task.status}`;
|
|
217
|
+
if (this.printedTasks.has(key)) return;
|
|
218
|
+
this.printedTasks.add(key);
|
|
219
|
+
const indentStr = " ".repeat(this.indent);
|
|
220
|
+
let line = "";
|
|
221
|
+
switch (task.status) {
|
|
222
|
+
case "running":
|
|
223
|
+
line = `${indentStr}[RUNNING] ${stripAnsi(task.text)}`;
|
|
224
|
+
break;
|
|
225
|
+
case "success":
|
|
226
|
+
line = `${indentStr}[SUCCESS] ${stripAnsi(task.finalText || task.text)}`;
|
|
227
|
+
break;
|
|
228
|
+
case "error":
|
|
229
|
+
line = `${indentStr}[ERROR] Failed: ${stripAnsi(task.text)}`;
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
console.log(line);
|
|
233
|
+
}
|
|
234
|
+
render() {
|
|
235
|
+
if (this.previousLineCount > 0) {
|
|
236
|
+
stdout.write(cursor.up(this.previousLineCount));
|
|
237
|
+
}
|
|
238
|
+
const spinner = SPINNER_FRAMES[this.frameIndex];
|
|
239
|
+
const indentStr = " ".repeat(this.indent);
|
|
240
|
+
const visibleTasks = Array.from(this.tasks.values()).filter(
|
|
241
|
+
(task) => task.status !== "pending"
|
|
242
|
+
);
|
|
243
|
+
for (const task of visibleTasks) {
|
|
244
|
+
stdout.write(`\r${cursor.clearLine}`);
|
|
245
|
+
let line = "";
|
|
246
|
+
switch (task.status) {
|
|
247
|
+
case "running":
|
|
248
|
+
line = `${indentStr}${colors.blue}${spinner}${styles.reset} ${task.text}`;
|
|
249
|
+
break;
|
|
250
|
+
case "success":
|
|
251
|
+
line = `${indentStr}${colors.green}${CHECK_MARK}${styles.reset} ${task.finalText || task.text}`;
|
|
252
|
+
break;
|
|
253
|
+
case "error":
|
|
254
|
+
line = `${indentStr}${colors.red}${CROSS_MARK}${styles.reset} Failed: ${task.text}`;
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
console.log(line);
|
|
258
|
+
}
|
|
259
|
+
this.previousLineCount = visibleTasks.length;
|
|
260
|
+
this.renderCount++;
|
|
261
|
+
}
|
|
262
|
+
stop() {
|
|
263
|
+
if (this.intervalId) {
|
|
264
|
+
clearInterval(this.intervalId);
|
|
265
|
+
this.intervalId = null;
|
|
266
|
+
}
|
|
267
|
+
if (isInteractive) {
|
|
268
|
+
this.render();
|
|
269
|
+
stdout.write(cursor.show);
|
|
270
|
+
} else {
|
|
271
|
+
this.tasks.forEach((task, taskId) => {
|
|
272
|
+
if (task.status !== "pending") {
|
|
273
|
+
this.renderNonInteractive(taskId, task);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// ../utils/src/log.ts
|
|
281
|
+
function formatDuration(ms) {
|
|
282
|
+
const duration = ms < 1e3 ? `${Math.round(ms)}ms` : `${(ms / 1e3).toFixed(2)}s`;
|
|
283
|
+
return bold(dim(`[${duration}]`));
|
|
284
|
+
}
|
|
285
|
+
async function runStep(text, action, successText, options) {
|
|
286
|
+
const effectiveAction = action ?? (async () => void 0);
|
|
287
|
+
const hasAction = action !== void 0;
|
|
288
|
+
const indent = options?.indent ?? 0;
|
|
289
|
+
const spinner = new Spinner(["task"], [text], { indent });
|
|
290
|
+
if (!options?.silent) {
|
|
291
|
+
spinner.start();
|
|
292
|
+
spinner.updateTask("task", "running");
|
|
293
|
+
}
|
|
294
|
+
const startTime = performance.now();
|
|
295
|
+
try {
|
|
296
|
+
const result = await effectiveAction();
|
|
297
|
+
if (options?.silent) {
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
const duration = performance.now() - startTime;
|
|
301
|
+
let finalSuccessText;
|
|
302
|
+
if (typeof successText === "function") {
|
|
303
|
+
finalSuccessText = successText(result);
|
|
304
|
+
} else {
|
|
305
|
+
finalSuccessText = successText ?? text;
|
|
306
|
+
}
|
|
307
|
+
finalSuccessText = bold(finalSuccessText);
|
|
308
|
+
if (hasAction) {
|
|
309
|
+
const durationText = formatDuration(duration);
|
|
310
|
+
finalSuccessText = `${finalSuccessText} ${durationText}`;
|
|
311
|
+
}
|
|
312
|
+
spinner.updateTask("task", "success", finalSuccessText);
|
|
313
|
+
spinner.stop();
|
|
314
|
+
return result;
|
|
315
|
+
} catch (error) {
|
|
316
|
+
if (!options?.silent) {
|
|
317
|
+
spinner.updateTask("task", "error");
|
|
318
|
+
spinner.stop();
|
|
319
|
+
}
|
|
320
|
+
console.error("");
|
|
321
|
+
if (error && typeof error === "object" && "exitCode" in error && typeof error.exitCode === "number") {
|
|
322
|
+
console.error(` Exit Code: ${error.exitCode}`);
|
|
323
|
+
const stdout2 = error.stdout?.toString().trim();
|
|
324
|
+
const stderr = error.stderr?.toString().trim();
|
|
325
|
+
const indent2 = (str) => str.split("\n").map((line) => ` ${line}`).join("\n");
|
|
326
|
+
if (stdout2) console.error(` Stdout:
|
|
327
|
+
${indent2(stdout2)}`);
|
|
328
|
+
if (stderr) console.error(` Stderr:
|
|
329
|
+
${indent2(stderr)}`);
|
|
330
|
+
} else {
|
|
331
|
+
console.error(` Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
332
|
+
}
|
|
333
|
+
console.error("");
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ../utils/src/package-manager.ts
|
|
339
|
+
import { execSync } from "child_process";
|
|
340
|
+
import { existsSync as existsSync2 } from "fs";
|
|
341
|
+
import { join as join2 } from "path";
|
|
342
|
+
function isCommandAvailable(command) {
|
|
343
|
+
try {
|
|
344
|
+
execSync(`command -v ${command}`, { stdio: "ignore" });
|
|
345
|
+
return true;
|
|
346
|
+
} catch {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
function detectPackageManager(cwd = process.cwd()) {
|
|
351
|
+
if (existsSync2(join2(cwd, "bun.lock")) || existsSync2(join2(cwd, "bun.lockb"))) {
|
|
352
|
+
return "bun";
|
|
353
|
+
}
|
|
354
|
+
if (existsSync2(join2(cwd, "pnpm-lock.yaml"))) {
|
|
355
|
+
return "pnpm";
|
|
356
|
+
}
|
|
357
|
+
if (existsSync2(join2(cwd, "yarn.lock"))) {
|
|
358
|
+
return "yarn";
|
|
359
|
+
}
|
|
360
|
+
if (existsSync2(join2(cwd, "package-lock.json"))) {
|
|
361
|
+
return "npm";
|
|
362
|
+
}
|
|
363
|
+
return detectByCommandAvailability();
|
|
364
|
+
}
|
|
365
|
+
function detectByCommandAvailability() {
|
|
366
|
+
if (isCommandAvailable("bun")) {
|
|
367
|
+
return "bun";
|
|
368
|
+
}
|
|
369
|
+
if (isCommandAvailable("pnpm")) {
|
|
370
|
+
return "pnpm";
|
|
371
|
+
}
|
|
372
|
+
if (isCommandAvailable("yarn")) {
|
|
373
|
+
return "yarn";
|
|
374
|
+
}
|
|
375
|
+
return "npm";
|
|
376
|
+
}
|
|
377
|
+
function getRunCommand(pm, script) {
|
|
378
|
+
switch (pm) {
|
|
379
|
+
case "bun":
|
|
380
|
+
return `bun run ${script}`;
|
|
381
|
+
case "pnpm":
|
|
382
|
+
return `pnpm run ${script}`;
|
|
383
|
+
case "yarn":
|
|
384
|
+
return `yarn run ${script}`;
|
|
385
|
+
case "npm":
|
|
386
|
+
default:
|
|
387
|
+
return `npm run ${script}`;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// src/lib/core/client.ts
|
|
392
|
+
import { PlaycademyClient } from "@playcademy/sdk";
|
|
393
|
+
|
|
394
|
+
// src/lib/core/context.ts
|
|
395
|
+
var context = {};
|
|
396
|
+
var cache = {
|
|
397
|
+
packageManager: null
|
|
398
|
+
};
|
|
399
|
+
function getWorkspace() {
|
|
400
|
+
return context.workspace || process.cwd();
|
|
401
|
+
}
|
|
402
|
+
function getPackageManager() {
|
|
403
|
+
if (cache.packageManager) return cache.packageManager;
|
|
404
|
+
cache.packageManager = detectPackageManager(getWorkspace());
|
|
405
|
+
return cache.packageManager;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// src/lib/core/logger.ts
|
|
409
|
+
import {
|
|
410
|
+
blue,
|
|
411
|
+
bold as bold2,
|
|
412
|
+
cyan,
|
|
413
|
+
dim as dim2,
|
|
414
|
+
gray,
|
|
415
|
+
green,
|
|
416
|
+
greenBright,
|
|
417
|
+
red,
|
|
418
|
+
yellow,
|
|
419
|
+
yellowBright
|
|
420
|
+
} from "colorette";
|
|
421
|
+
import { colorize } from "json-colorizer";
|
|
422
|
+
function customTransform(text) {
|
|
423
|
+
const highlightCode = (text2) => text2.replace(/`([^`]+)`/g, (_, code) => greenBright(code));
|
|
424
|
+
return highlightCode(text);
|
|
425
|
+
}
|
|
426
|
+
function formatTable(data, title) {
|
|
427
|
+
if (data.length === 0) return;
|
|
428
|
+
const keys = Object.keys(data[0]);
|
|
429
|
+
const rows = data.map((item) => keys.map((key) => String(item[key] ?? "")));
|
|
430
|
+
const widths = keys.map((key, i) => {
|
|
431
|
+
const headerWidth = key.length;
|
|
432
|
+
const dataWidth = Math.max(...rows.map((row) => row[i].length));
|
|
433
|
+
return Math.max(headerWidth, dataWidth);
|
|
434
|
+
});
|
|
435
|
+
const totalWidth = widths.reduce((sum, w) => sum + w + 3, -1);
|
|
436
|
+
const separator = "\u251C" + widths.map((w) => "\u2500".repeat(w + 2)).join("\u253C") + "\u2524";
|
|
437
|
+
const topBorder = "\u250C" + "\u2500".repeat(totalWidth) + "\u2510";
|
|
438
|
+
const titleSeparator = "\u251C" + widths.map((w) => "\u2500".repeat(w + 2)).join("\u252C") + "\u2524";
|
|
439
|
+
const bottomBorder = "\u2514" + widths.map((w) => "\u2500".repeat(w + 2)).join("\u2534") + "\u2518";
|
|
440
|
+
console.log(topBorder);
|
|
441
|
+
if (title) {
|
|
442
|
+
const titleText = bold2(title);
|
|
443
|
+
const titlePadding = totalWidth - title.length - 1;
|
|
444
|
+
const titleRow = "\u2502 " + titleText + " ".repeat(titlePadding) + "\u2502";
|
|
445
|
+
console.log(titleRow);
|
|
446
|
+
console.log(titleSeparator);
|
|
447
|
+
}
|
|
448
|
+
const header = "\u2502 " + keys.map((key, i) => key.padEnd(widths[i])).join(" \u2502 ") + " \u2502";
|
|
449
|
+
console.log(header);
|
|
450
|
+
console.log(separator);
|
|
451
|
+
rows.forEach((row) => {
|
|
452
|
+
const dataRow = "\u2502 " + row.map((cell, i) => cell.padEnd(widths[i])).join(" \u2502 ") + " \u2502";
|
|
453
|
+
console.log(dataRow);
|
|
454
|
+
});
|
|
455
|
+
console.log(bottomBorder);
|
|
456
|
+
}
|
|
457
|
+
var logger = {
|
|
458
|
+
table: (data, title) => {
|
|
459
|
+
formatTable(data, title);
|
|
460
|
+
},
|
|
461
|
+
/**
|
|
462
|
+
* Info message - general information
|
|
463
|
+
*/
|
|
464
|
+
info: (message, indent = 0) => {
|
|
465
|
+
const spaces = " ".repeat(indent);
|
|
466
|
+
console.log(`${spaces}${blue("\u2139")} ${bold2(customTransform(message))}`);
|
|
467
|
+
},
|
|
468
|
+
/**
|
|
469
|
+
* Admonition - highlighted note/tip/warning box (Docusaurus-style)
|
|
470
|
+
*/
|
|
471
|
+
admonition: (type, title, lines, indent = 0) => {
|
|
472
|
+
const spaces = " ".repeat(indent);
|
|
473
|
+
const configs = {
|
|
474
|
+
note: { color: green },
|
|
475
|
+
tip: { color: cyan },
|
|
476
|
+
info: { color: blue },
|
|
477
|
+
warning: { color: yellow }
|
|
478
|
+
};
|
|
479
|
+
const { color: color2 } = configs[type];
|
|
480
|
+
console.log(`${spaces}${color2("\u250C\u2500")} ${bold2(color2(title.toUpperCase()))}`);
|
|
481
|
+
if (lines && lines.length > 0) {
|
|
482
|
+
lines.forEach((line) => {
|
|
483
|
+
console.log(`${spaces}${color2("\u2502")} ${customTransform(line)}`);
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
console.log(`${spaces}${color2("\u2514\u2500")}`);
|
|
487
|
+
},
|
|
488
|
+
/**
|
|
489
|
+
* Dim message - less important information
|
|
490
|
+
*/
|
|
491
|
+
dim: (message, indent = 0) => {
|
|
492
|
+
const spaces = " ".repeat(indent);
|
|
493
|
+
console.log(`${spaces}${dim2(customTransform(message))}`);
|
|
494
|
+
},
|
|
495
|
+
/**
|
|
496
|
+
* Success message - operation completed successfully
|
|
497
|
+
*/
|
|
498
|
+
success: (message, indent = 0) => {
|
|
499
|
+
const spaces = " ".repeat(indent);
|
|
500
|
+
console.log(`${spaces}${green("\u2714")} ${bold2(customTransform(message))}`);
|
|
501
|
+
},
|
|
502
|
+
remark: (message, indent = 0) => {
|
|
503
|
+
const spaces = " ".repeat(indent);
|
|
504
|
+
console.log(`${spaces}${bold2(yellowBright("\u2726"))} ${bold2(customTransform(message))}`);
|
|
505
|
+
},
|
|
506
|
+
/**
|
|
507
|
+
* Error message - operation failed
|
|
508
|
+
*/
|
|
509
|
+
error: (message, indent = 0) => {
|
|
510
|
+
const spaces = " ".repeat(indent);
|
|
511
|
+
console.error(`${spaces}${red("\u2716")} ${customTransform(message)}`);
|
|
512
|
+
},
|
|
513
|
+
bold: (message, indent = 0) => {
|
|
514
|
+
const spaces = " ".repeat(indent);
|
|
515
|
+
console.log(`${spaces}${bold2(customTransform(message))}`);
|
|
516
|
+
},
|
|
517
|
+
/**
|
|
518
|
+
* Warning message - something to be aware of
|
|
519
|
+
*/
|
|
520
|
+
warn: (message, indent = 0) => {
|
|
521
|
+
const spaces = " ".repeat(indent);
|
|
522
|
+
console.warn(`${spaces}${yellow("\u26A0")} ${bold2(customTransform(message))}`);
|
|
523
|
+
},
|
|
524
|
+
/**
|
|
525
|
+
* Debug message - only shown when DEBUG env var is set
|
|
526
|
+
*/
|
|
527
|
+
debug: (message, indent = 0) => {
|
|
528
|
+
const spaces = " ".repeat(indent);
|
|
529
|
+
if (process.env.DEBUG) {
|
|
530
|
+
console.log(gray("[DEBUG]"), `${spaces}${message}`);
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
/**
|
|
534
|
+
* Step message - shows progress through a process
|
|
535
|
+
*/
|
|
536
|
+
step: (step, total, message, indent = 0) => {
|
|
537
|
+
const spaces = " ".repeat(indent);
|
|
538
|
+
console.log(spaces + cyan(`[${step}/${total}]`), customTransform(message));
|
|
539
|
+
},
|
|
540
|
+
/**
|
|
541
|
+
* Highlighted message - draws attention
|
|
542
|
+
*/
|
|
543
|
+
highlight: (message, indent = 0) => {
|
|
544
|
+
const spaces = " ".repeat(indent);
|
|
545
|
+
console.log(bold2(`${spaces}${cyan(customTransform(message))}`));
|
|
546
|
+
},
|
|
547
|
+
/**
|
|
548
|
+
* Aside message - for side information
|
|
549
|
+
*/
|
|
550
|
+
aside: (message, indent = 0) => {
|
|
551
|
+
const spaces = " ".repeat(indent);
|
|
552
|
+
console.log(`${spaces}${bold2(customTransform(message))}`);
|
|
553
|
+
},
|
|
554
|
+
/**
|
|
555
|
+
* Data display - for structured data output
|
|
556
|
+
*/
|
|
557
|
+
data: (label, value, indent = 0) => {
|
|
558
|
+
const spaces = " ".repeat(indent);
|
|
559
|
+
if (value !== void 0) {
|
|
560
|
+
console.log(`${spaces}${dim2(label + ":")} ${bold2(value)}`);
|
|
561
|
+
} else {
|
|
562
|
+
console.log(`${spaces}${dim2(label)}`);
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
/**
|
|
566
|
+
* JSON output - pretty-printed JSON
|
|
567
|
+
*/
|
|
568
|
+
json: (data, indent = 0) => {
|
|
569
|
+
const spaces = " ".repeat(indent);
|
|
570
|
+
const jsonString = colorize(JSON.stringify(data, null, 2));
|
|
571
|
+
jsonString.split("\n").forEach((line) => {
|
|
572
|
+
console.log(`${spaces}${line}`);
|
|
573
|
+
});
|
|
574
|
+
},
|
|
575
|
+
/**
|
|
576
|
+
* New line
|
|
577
|
+
*/
|
|
578
|
+
newLine: () => {
|
|
579
|
+
console.log();
|
|
580
|
+
},
|
|
581
|
+
/**
|
|
582
|
+
* Raw output - no formatting, useful for ASCII art or pre-formatted text
|
|
583
|
+
*/
|
|
584
|
+
raw: (text, indent = 0) => {
|
|
585
|
+
const spaces = " ".repeat(indent);
|
|
586
|
+
console.log(`${spaces}${text}`);
|
|
587
|
+
},
|
|
588
|
+
/**
|
|
589
|
+
* Display a configuration error with helpful suggestions
|
|
590
|
+
*/
|
|
591
|
+
configError: (error, indent = 0) => {
|
|
592
|
+
const spaces = " ".repeat(indent);
|
|
593
|
+
const isConfigError = error && typeof error === "object" && "name" in error && error.name === "ConfigError";
|
|
594
|
+
if (isConfigError && "message" in error && "field" in error && "suggestion" in error) {
|
|
595
|
+
const configErr = error;
|
|
596
|
+
console.error(`${spaces}${red("\u2716")} ${bold2(configErr.message)}`);
|
|
597
|
+
if (configErr.field) {
|
|
598
|
+
console.error(`${spaces} ${dim2("Field:")} ${configErr.field}`);
|
|
599
|
+
}
|
|
600
|
+
if (configErr.suggestion) {
|
|
601
|
+
console.error(`${spaces} ${dim2("Fix:")} ${configErr.suggestion}`);
|
|
602
|
+
}
|
|
603
|
+
} else if (error instanceof Error) {
|
|
604
|
+
console.error(`${spaces}${red("\u2716")} ${bold2(error.message)}`);
|
|
605
|
+
} else {
|
|
606
|
+
console.error(`${spaces}${red("\u2716")} ${bold2(String(error))}`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
// src/lib/core/errors.ts
|
|
612
|
+
function getErrorMessage(error) {
|
|
613
|
+
if (error instanceof Error) return error.message;
|
|
614
|
+
if (typeof error === "string") return error;
|
|
615
|
+
if (error && typeof error === "object" && "message" in error) {
|
|
616
|
+
return String(error.message);
|
|
617
|
+
}
|
|
618
|
+
return "Unknown error";
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
// ../utils/src/pure/index.ts
|
|
622
|
+
init_package_json();
|
|
623
|
+
|
|
624
|
+
// src/lib/config/loader.ts
|
|
625
|
+
init_file_loader();
|
|
626
|
+
|
|
627
|
+
// src/lib/templates/loader.ts
|
|
628
|
+
import { dirname, resolve } from "path";
|
|
629
|
+
import { fileURLToPath } from "url";
|
|
630
|
+
var currentDir = dirname(fileURLToPath(import.meta.url));
|
|
631
|
+
|
|
632
|
+
// src/lib/db/reset.ts
|
|
633
|
+
function resetDatabase() {
|
|
634
|
+
const dbPath = getDevDbPath();
|
|
635
|
+
const sqlite = new Database2(dbPath);
|
|
636
|
+
const TURN_FOREIGN_KEYS_OFF_SQL = "PRAGMA foreign_keys = OFF";
|
|
637
|
+
const TURN_FOREIGN_KEYS_ON_SQL = "PRAGMA foreign_keys = ON";
|
|
638
|
+
const DROP_ALL_USER_TABLES_SQL = `SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'`;
|
|
639
|
+
const DROP_TABLE_SQL = (tableName) => `DROP TABLE IF EXISTS ${tableName}`;
|
|
640
|
+
runStep(
|
|
641
|
+
"Dropping all tables",
|
|
642
|
+
async () => {
|
|
643
|
+
sqlite.prepare(TURN_FOREIGN_KEYS_OFF_SQL).run();
|
|
644
|
+
const tables = sqlite.prepare(DROP_ALL_USER_TABLES_SQL).all();
|
|
645
|
+
for (const table of tables) {
|
|
646
|
+
sqlite.prepare(DROP_TABLE_SQL(table.name)).run();
|
|
647
|
+
}
|
|
648
|
+
sqlite.prepare(TURN_FOREIGN_KEYS_ON_SQL).run();
|
|
649
|
+
},
|
|
650
|
+
"Dropped all tables"
|
|
651
|
+
);
|
|
652
|
+
try {
|
|
653
|
+
const pm = getPackageManager();
|
|
654
|
+
const pushCommand = getRunCommand(pm, "db:push");
|
|
655
|
+
runStep(
|
|
656
|
+
"Pushing schema",
|
|
657
|
+
async () => {
|
|
658
|
+
execSync2(pushCommand, { stdio: ["inherit", "ignore", "inherit"] });
|
|
659
|
+
},
|
|
660
|
+
"Pushed schema"
|
|
661
|
+
);
|
|
662
|
+
return sqlite;
|
|
663
|
+
} catch (error) {
|
|
664
|
+
logger.error(`Failed to push schema: ${getErrorMessage(error)}`);
|
|
665
|
+
process.exit(1);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
export {
|
|
669
|
+
getDevDbPath as getPath,
|
|
670
|
+
resetDatabase
|
|
671
|
+
};
|
|
@@ -2,37 +2,12 @@
|
|
|
2
2
|
* Constants for game backend workers
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
* Environment variables for game backend workers
|
|
7
|
-
*/
|
|
8
|
-
export const ENV_VARS = {
|
|
9
|
-
/** Game-specific API key for calling platform API */
|
|
10
|
-
PLAYCADEMY_API_KEY: 'PLAYCADEMY_API_KEY',
|
|
11
|
-
/** Game ID (UUID) */
|
|
12
|
-
GAME_ID: 'GAME_ID',
|
|
13
|
-
/** Platform API base URL */
|
|
14
|
-
PLAYCADEMY_BASE_URL: 'PLAYCADEMY_BASE_URL',
|
|
15
|
-
} as const
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Worker naming patterns
|
|
19
|
-
* - Production workers: {slug} → accessible at {slug}.playcademy.gg
|
|
20
|
-
* - Staging workers: staging-{slug} → accessible at {slug}-staging.playcademy.gg
|
|
21
|
-
*/
|
|
22
|
-
export const WORKER_NAMING = {
|
|
23
|
-
/** Prefix for staging worker names (e.g., "staging-bamboo") */
|
|
24
|
-
STAGING_PREFIX: 'staging-',
|
|
25
|
-
/** Suffix for staging worker hostnames (e.g., "bamboo-staging.playcademy.gg") */
|
|
26
|
-
STAGING_SUFFIX: '-staging',
|
|
27
|
-
} as const
|
|
5
|
+
import { TIMEBACK_ROUTES, WORKER_ENV_VARS, WORKER_NAMING } from '@playcademy/constants'
|
|
28
6
|
|
|
29
7
|
/**
|
|
30
|
-
*
|
|
31
|
-
* These are relative to /api/ - full paths are /api/integrations/timeback/*
|
|
8
|
+
* Re-export shared constants from @playcademy/constants
|
|
32
9
|
*/
|
|
33
|
-
|
|
34
|
-
END_ACTIVITY: '/integrations/timeback/end-activity',
|
|
35
|
-
} as const
|
|
10
|
+
export { WORKER_ENV_VARS as ENV_VARS, WORKER_NAMING }
|
|
36
11
|
|
|
37
12
|
/**
|
|
38
13
|
* Built-in API routes
|