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.
Files changed (30) hide show
  1. package/dist/constants.d.ts +72 -1
  2. package/dist/constants.js +46 -0
  3. package/dist/db.d.ts +61 -0
  4. package/dist/db.js +671 -0
  5. package/dist/edge-play/src/constants.ts +3 -28
  6. package/dist/{types.d.ts → index.d.ts} +118 -14
  7. package/dist/index.js +4650 -6635
  8. package/dist/templates/api/sample-route-with-db.ts.template +141 -0
  9. package/dist/templates/config/playcademy.config.js.template +4 -0
  10. package/dist/templates/config/playcademy.config.json.template +3 -0
  11. package/dist/templates/config/timeback-config.js.template +8 -0
  12. package/dist/templates/database/db-index.ts.template +21 -0
  13. package/dist/templates/database/db-schema-index.ts.template +8 -0
  14. package/dist/templates/database/db-schema-scores.ts.template +43 -0
  15. package/dist/templates/database/db-schema-users.ts.template +23 -0
  16. package/dist/templates/database/db-seed.ts.template +52 -0
  17. package/dist/templates/database/db-types.ts.template +21 -0
  18. package/dist/templates/database/drizzle-config.ts.template +13 -0
  19. package/dist/templates/database/package.json.template +20 -0
  20. package/dist/templates/gitignore.template +17 -0
  21. package/dist/templates/playcademy-gitignore.template +3 -0
  22. package/dist/utils.d.ts +31 -14
  23. package/dist/utils.js +523 -490
  24. package/package.json +19 -3
  25. package/dist/templates/backend-config.js.template +0 -6
  26. package/dist/templates/playcademy.config.js.template +0 -4
  27. package/dist/templates/playcademy.config.json.template +0 -3
  28. package/dist/templates/timeback-config.js.template +0 -17
  29. /package/dist/templates/{sample-route.ts → api/sample-route.ts.template} +0 -0
  30. /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
- * TimeBack integration route paths
31
- * These are relative to /api/ - full paths are /api/integrations/timeback/*
8
+ * Re-export shared constants from @playcademy/constants
32
9
  */
33
- const TIMEBACK_ROUTES = {
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