playcademy 0.14.3 → 0.14.4-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/utils.js CHANGED
@@ -294,262 +294,461 @@ var init_file_loader = __esm({
294
294
  }
295
295
  });
296
296
 
297
+ // src/lib/config/loader.ts
298
+ init_file_loader();
299
+ import { dirname as dirname2, resolve as resolve2 } from "path";
300
+
297
301
  // src/constants/api.ts
298
- var DEFAULT_API_ROUTES_DIRECTORY;
299
- var init_api = __esm({
300
- "src/constants/api.ts"() {
301
- "use strict";
302
- DEFAULT_API_ROUTES_DIRECTORY = "server/api";
302
+ var DEFAULT_API_ROUTES_DIRECTORY = "server/api";
303
+
304
+ // ../../package.json
305
+ var package_default = {
306
+ name: "playcademy",
307
+ devDependencies: {
308
+ "@aws-sdk/client-s3": "^3.787.0",
309
+ "@eslint/js": "^9.24.0",
310
+ "@ianvs/prettier-plugin-sort-imports": "^4.4.2",
311
+ "@pulumi/pulumi": "^3.195.0",
312
+ "@types/bun": "latest",
313
+ commander: "^14.0.0",
314
+ eslint: "^9.24.0",
315
+ globals: "^16.0.0",
316
+ husky: "^9.1.7",
317
+ jscodeshift: "^17.3.0",
318
+ "lint-staged": "^15.5.1",
319
+ prettier: "3.5.3",
320
+ "prettier-plugin-svelte": "^3.3.3",
321
+ rimraf: "^6.0.1",
322
+ sharp: "^0.34.2",
323
+ typedoc: "^0.28.5",
324
+ "typedoc-plugin-markdown": "^4.7.0",
325
+ "typedoc-vitepress-theme": "^1.1.2",
326
+ "typescript-eslint": "^8.30.1",
327
+ "yocto-spinner": "^0.2.2"
328
+ },
329
+ peerDependencies: {
330
+ typescript: "^5"
331
+ },
332
+ private: true,
333
+ scripts: {
334
+ build: "bun run scripts/build.ts",
335
+ "build:types": "bun tsc -b packages/data",
336
+ clean: "bun scripts/clean.ts && bun i",
337
+ "docs:commit": "bun scripts/docs-commit.ts",
338
+ dev: "bunx sst dev",
339
+ "dev:reset": "sst shell -- bun run scripts/dev-reset.ts",
340
+ "db:reseed": "sst shell -- bun run scripts/dev-reseed-db.ts",
341
+ "db:sync": "sst shell -- bun run scripts/dev-sync-db.ts",
342
+ "db:studio": "sst shell -- bun run --filter @playcademy/data studio",
343
+ "upload-games": "sst shell -- bun run scripts/upload-games.ts",
344
+ "upload-items": "sst shell -- bun run scripts/upload-items.ts",
345
+ "upload-sprites": "sst shell -- bun run scripts/upload-sprites.ts",
346
+ "upload-static-assets": "sst shell -- bun run scripts/upload-static-assets.ts",
347
+ "upload:all": "sst shell -- bun run scripts/upload-all.ts",
348
+ "sync-engine-assets": "bun scripts/sync-engine-assets.ts",
349
+ "sync-vite-templates": "bun scripts/sync-vite-templates.ts",
350
+ "sync-godot-template": "bun scripts/sync-godot-template.ts",
351
+ "sync:all": "bun scripts/sync-all.ts",
352
+ "setup-cloudflare": "bun scripts/setup-cloudflare-dispatch.ts",
353
+ "list-s3-bucket": "sst shell -- bun scripts/list-s3-bucket.ts",
354
+ doctor: "bunx sst shell -- bun scripts/doctor.ts",
355
+ format: "bun run --filter '*' format",
356
+ lint: "bun run --filter '*' lint",
357
+ prepare: "husky",
358
+ sort: "bunx sort-package-json **/package.json",
359
+ test: "bun test",
360
+ "test:unit": "bun scripts/test-unit.ts",
361
+ "test:integration": "bun scripts/test-integration.ts",
362
+ "test:watch": "bun test --watch",
363
+ "docker:relay": "bun run scripts/docker-relay.ts",
364
+ "debug:leaderboard-notifications": "bunx sst shell -- bun scripts/debug/leaderboard-notifications.ts",
365
+ "timeback:caliper": "sst shell -- bun scripts/caliper-cli.ts",
366
+ notify: "NODE_ENV=productionbunx sst shell -- bun scripts/notifications-cli.ts"
367
+ },
368
+ type: "module",
369
+ workspaces: {
370
+ packages: [
371
+ "apps/*",
372
+ "packages/*",
373
+ "games/*",
374
+ "templates/*"
375
+ ],
376
+ catalog: {
377
+ sst: "^3.14.11",
378
+ dedent: "^1.6.0",
379
+ typescript: "^5.7.2",
380
+ vite: "^6.3.5",
381
+ eslint: "^9.24.0",
382
+ prettier: "3.5.3",
383
+ "@types/bun": "latest",
384
+ jose: "^5.2.3",
385
+ zod: "^3.25.53",
386
+ "better-auth": "1.3.33"
387
+ },
388
+ catalogs: {
389
+ svelte: {
390
+ svelte: "^5.23.1",
391
+ "@sveltejs/adapter-auto": "^6.0.0",
392
+ "@sveltejs/kit": "^2.16.0",
393
+ "@sveltejs/vite-plugin-svelte": "^5.0.3",
394
+ "svelte-check": "^4.2.1",
395
+ "prettier-plugin-svelte": "^3.3.3",
396
+ "eslint-plugin-svelte": "^3.0.0"
397
+ },
398
+ database: {
399
+ "drizzle-orm": "^0.42.0",
400
+ "drizzle-kit": "^0.31.0",
401
+ "drizzle-zod": "^0.7.1"
402
+ },
403
+ aws: {
404
+ "@aws-sdk/client-s3": "^3.787.0",
405
+ "@aws-sdk/s3-request-presigner": "^3.787.0"
406
+ },
407
+ linting: {
408
+ "typescript-eslint": "^8.30.1",
409
+ "@eslint/js": "^9.24.0",
410
+ "eslint-config-prettier": "^10.0.1"
411
+ }
412
+ }
303
413
  }
304
- });
414
+ };
415
+
416
+ // src/constants/auth.ts
417
+ var BETTER_AUTH_VERSION = package_default.workspaces.catalog["better-auth"];
305
418
 
306
419
  // src/constants/config.ts
307
- var ENV_FILES, TSCONFIG_FILES;
308
- var init_config = __esm({
309
- "src/constants/config.ts"() {
310
- "use strict";
311
- ENV_FILES = [
312
- ".env",
313
- // Loaded first
314
- ".env.development",
315
- // Overrides .env
316
- ".env.local"
317
- // Overrides all (highest priority)
318
- ];
319
- TSCONFIG_FILES = [
320
- "tsconfig.app.json",
321
- // Modern tooling (try first)
322
- "tsconfig.json"
323
- // Standard (fallback)
324
- ];
325
- }
326
- });
420
+ var ENV_FILES = [
421
+ ".env",
422
+ // Loaded first
423
+ ".env.development",
424
+ // Overrides .env
425
+ ".env.local"
426
+ // Overrides all (highest priority)
427
+ ];
428
+ var TSCONFIG_FILES = [
429
+ "tsconfig.app.json",
430
+ // Modern tooling (try first)
431
+ "tsconfig.json"
432
+ // Standard (fallback)
433
+ ];
327
434
 
328
435
  // src/constants/bucket.ts
329
- var BUCKET_ALWAYS_SKIP;
330
- var init_bucket = __esm({
331
- "src/constants/bucket.ts"() {
332
- "use strict";
333
- init_config();
334
- BUCKET_ALWAYS_SKIP = [".git", ".DS_Store", ".gitignore", ...ENV_FILES];
335
- }
336
- });
436
+ var BUCKET_ALWAYS_SKIP = [".git", ".DS_Store", ".gitignore", ...ENV_FILES];
337
437
 
338
438
  // src/constants/cloudflare.ts
339
- var CLOUDFLARE_COMPATIBILITY_DATE, CLOUDFLARE_BINDINGS;
340
- var init_cloudflare = __esm({
341
- "src/constants/cloudflare.ts"() {
342
- "use strict";
343
- CLOUDFLARE_COMPATIBILITY_DATE = "2024-01-01";
344
- CLOUDFLARE_BINDINGS = {
345
- /** R2 bucket binding name */
346
- BUCKET: "BUCKET",
347
- /** KV namespace binding name */
348
- KV: "KV",
349
- /** D1 database binding name */
350
- DB: "DB"
351
- };
352
- }
353
- });
354
-
355
- // src/constants/database.ts
356
- var init_database = __esm({
357
- "src/constants/database.ts"() {
358
- "use strict";
359
- }
360
- });
361
-
362
- // src/constants/http-server.ts
363
- var init_http_server = __esm({
364
- "src/constants/http-server.ts"() {
365
- "use strict";
366
- }
367
- });
439
+ var CLOUDFLARE_COMPATIBILITY_DATE = "2024-01-01";
440
+ var CLOUDFLARE_BINDINGS = {
441
+ /** R2 bucket binding name */
442
+ BUCKET: "BUCKET",
443
+ /** KV namespace binding name */
444
+ KV: "KV",
445
+ /** D1 database binding name */
446
+ DB: "DB"
447
+ };
368
448
 
369
449
  // src/constants/paths.ts
370
450
  import { join } from "path";
371
- var WORKSPACE_NAME, CLI_DIRECTORIES;
372
- var init_paths = __esm({
373
- "src/constants/paths.ts"() {
374
- "use strict";
375
- WORKSPACE_NAME = ".playcademy";
376
- CLI_DIRECTORIES = {
377
- /** Root directory for CLI artifacts in workspace */
378
- WORKSPACE: WORKSPACE_NAME,
379
- /** Database directory within workspace */
380
- DATABASE: join(WORKSPACE_NAME, "db"),
381
- /** KV storage directory within workspace */
382
- KV: join(WORKSPACE_NAME, "kv"),
383
- /** Bucket storage directory within workspace */
384
- BUCKET: join(WORKSPACE_NAME, "bucket")
385
- };
386
- }
387
- });
451
+ var WORKSPACE_NAME = ".playcademy";
452
+ var CLI_DIRECTORIES = {
453
+ /** Root directory for CLI artifacts in workspace */
454
+ WORKSPACE: WORKSPACE_NAME,
455
+ /** Database directory within workspace */
456
+ DATABASE: join(WORKSPACE_NAME, "db"),
457
+ /** KV storage directory within workspace */
458
+ KV: join(WORKSPACE_NAME, "kv"),
459
+ /** Bucket storage directory within workspace */
460
+ BUCKET: join(WORKSPACE_NAME, "bucket")
461
+ };
388
462
 
389
463
  // src/constants/ports.ts
390
- var DEFAULT_PORTS;
391
- var init_ports = __esm({
392
- "src/constants/ports.ts"() {
393
- "use strict";
394
- DEFAULT_PORTS = {
395
- /** Sandbox server (mock platform API) */
396
- SANDBOX: 4321,
397
- /** Backend dev server (game backend with HMR) */
398
- BACKEND: 8788
399
- };
400
- }
401
- });
464
+ var DEFAULT_PORTS = {
465
+ /** Sandbox server (mock platform API) */
466
+ SANDBOX: 4321,
467
+ /** Backend dev server (game backend with HMR) */
468
+ BACKEND: 8788
469
+ };
402
470
 
403
471
  // src/constants/timeback.ts
404
- var CONFIG_FILE_NAMES;
405
- var init_timeback = __esm({
406
- "src/constants/timeback.ts"() {
407
- "use strict";
408
- CONFIG_FILE_NAMES = [
409
- "playcademy.config.js",
410
- "playcademy.config.json",
411
- "playcademy.config.mjs"
412
- ];
413
- }
414
- });
415
-
416
- // ../constants/src/auth.ts
417
- var init_auth = __esm({
418
- "../constants/src/auth.ts"() {
419
- "use strict";
420
- }
421
- });
422
-
423
- // ../constants/src/domains.ts
424
- var init_domains = __esm({
425
- "../constants/src/domains.ts"() {
426
- "use strict";
427
- }
428
- });
429
-
430
- // ../constants/src/env-vars.ts
431
- var init_env_vars = __esm({
432
- "../constants/src/env-vars.ts"() {
433
- "use strict";
434
- }
435
- });
472
+ var CONFIG_FILE_NAMES = [
473
+ "playcademy.config.js",
474
+ "playcademy.config.json",
475
+ "playcademy.config.mjs"
476
+ ];
436
477
 
437
478
  // ../constants/src/overworld.ts
438
- var ITEM_SLUGS, CURRENCIES, BADGES, CORE_GAME_UUIDS;
439
- var init_overworld = __esm({
440
- "../constants/src/overworld.ts"() {
441
- "use strict";
442
- ITEM_SLUGS = {
443
- /** Primary platform currency */
444
- PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
445
- /** Experience points currency */
446
- PLAYCADEMY_XP: "PLAYCADEMY_XP",
447
- /** Core platform badges */
448
- FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
449
- EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
450
- FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
451
- /** Example items */
452
- COMMON_SWORD: "COMMON_SWORD",
453
- SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
454
- SMALL_BACKPACK: "SMALL_BACKPACK",
455
- /** Placeable items */
456
- LAVA_LAMP: "LAVA_LAMP",
457
- BOOMBOX: "BOOMBOX",
458
- CABIN_BED: "CABIN_BED"
459
- };
460
- CURRENCIES = {
461
- /** Primary platform currency slug */
462
- PRIMARY: ITEM_SLUGS.PLAYCADEMY_CREDITS,
463
- /** Experience points slug */
464
- XP: ITEM_SLUGS.PLAYCADEMY_XP
465
- };
466
- BADGES = {
467
- FOUNDING_MEMBER: ITEM_SLUGS.FOUNDING_MEMBER_BADGE,
468
- EARLY_ADOPTER: ITEM_SLUGS.EARLY_ADOPTER_BADGE,
469
- FIRST_GAME: ITEM_SLUGS.FIRST_GAME_BADGE
470
- };
471
- CORE_GAME_UUIDS = {
472
- /** Internal playground game for development and testing */
473
- PLAYGROUND: "00000000-0000-0000-0000-000000000001"
474
- };
475
- }
476
- });
477
-
478
- // ../constants/src/system.ts
479
- var init_system = __esm({
480
- "../constants/src/system.ts"() {
481
- "use strict";
482
- }
483
- });
484
-
485
- // ../constants/src/timeback.ts
486
- var init_timeback2 = __esm({
487
- "../constants/src/timeback.ts"() {
488
- "use strict";
489
- }
490
- });
491
-
492
- // ../constants/src/workers.ts
493
- var init_workers = __esm({
494
- "../constants/src/workers.ts"() {
495
- "use strict";
496
- }
497
- });
498
-
499
- // ../constants/src/index.ts
500
- var init_src = __esm({
501
- "../constants/src/index.ts"() {
502
- "use strict";
503
- init_auth();
504
- init_domains();
505
- init_env_vars();
506
- init_overworld();
507
- init_system();
508
- init_timeback2();
509
- init_workers();
510
- }
511
- });
512
-
513
- // src/constants/urls.ts
514
- var init_urls = __esm({
515
- "src/constants/urls.ts"() {
516
- "use strict";
517
- init_src();
518
- }
519
- });
479
+ var ITEM_SLUGS = {
480
+ /** Primary platform currency */
481
+ PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
482
+ /** Experience points currency */
483
+ PLAYCADEMY_XP: "PLAYCADEMY_XP",
484
+ /** Core platform badges */
485
+ FOUNDING_MEMBER_BADGE: "FOUNDING_MEMBER_BADGE",
486
+ EARLY_ADOPTER_BADGE: "EARLY_ADOPTER_BADGE",
487
+ FIRST_GAME_BADGE: "FIRST_GAME_BADGE",
488
+ /** Example items */
489
+ COMMON_SWORD: "COMMON_SWORD",
490
+ SMALL_HEALTH_POTION: "SMALL_HEALTH_POTION",
491
+ SMALL_BACKPACK: "SMALL_BACKPACK",
492
+ /** Placeable items */
493
+ LAVA_LAMP: "LAVA_LAMP",
494
+ BOOMBOX: "BOOMBOX",
495
+ CABIN_BED: "CABIN_BED"
496
+ };
497
+ var CURRENCIES = {
498
+ /** Primary platform currency slug */
499
+ PRIMARY: ITEM_SLUGS.PLAYCADEMY_CREDITS,
500
+ /** Experience points slug */
501
+ XP: ITEM_SLUGS.PLAYCADEMY_XP
502
+ };
503
+ var BADGES = {
504
+ FOUNDING_MEMBER: ITEM_SLUGS.FOUNDING_MEMBER_BADGE,
505
+ EARLY_ADOPTER: ITEM_SLUGS.EARLY_ADOPTER_BADGE,
506
+ FIRST_GAME: ITEM_SLUGS.FIRST_GAME_BADGE
507
+ };
508
+ var CORE_GAME_UUIDS = {
509
+ /** Internal playground game for development and testing */
510
+ PLAYGROUND: "00000000-0000-0000-0000-000000000001"
511
+ };
520
512
 
521
- // src/constants/index.ts
522
- var init_constants = __esm({
523
- "src/constants/index.ts"() {
524
- "use strict";
525
- init_api();
526
- init_bucket();
527
- init_cloudflare();
528
- init_config();
529
- init_database();
530
- init_http_server();
531
- init_paths();
532
- init_ports();
533
- init_timeback();
534
- init_urls();
513
+ // src/lib/core/logger.ts
514
+ import {
515
+ blue,
516
+ blueBright,
517
+ bold,
518
+ cyan,
519
+ dim,
520
+ gray,
521
+ green,
522
+ greenBright,
523
+ red,
524
+ yellow,
525
+ yellowBright
526
+ } from "colorette";
527
+ import { colorize } from "json-colorizer";
528
+ function customTransform(text) {
529
+ let result = text;
530
+ result = result.replace(/`([^`]+)`/g, (_, code) => greenBright(code));
531
+ result = result.replace(/<([^>]+)>/g, (_, path2) => blueBright(path2));
532
+ return result;
533
+ }
534
+ function formatTable(data, title) {
535
+ if (data.length === 0) return;
536
+ const keys = Object.keys(data[0]);
537
+ const rows = data.map((item) => keys.map((key) => String(item[key] ?? "")));
538
+ const widths = keys.map((key, i) => {
539
+ const headerWidth = key.length;
540
+ const dataWidth = Math.max(...rows.map((row) => row[i].length));
541
+ return Math.max(headerWidth, dataWidth);
542
+ });
543
+ const totalWidth = widths.reduce((sum, w) => sum + w + 3, -1);
544
+ const separator = "\u251C" + widths.map((w) => "\u2500".repeat(w + 2)).join("\u253C") + "\u2524";
545
+ const topBorder = "\u250C" + "\u2500".repeat(totalWidth) + "\u2510";
546
+ const titleSeparator = "\u251C" + widths.map((w) => "\u2500".repeat(w + 2)).join("\u252C") + "\u2524";
547
+ const bottomBorder = "\u2514" + widths.map((w) => "\u2500".repeat(w + 2)).join("\u2534") + "\u2518";
548
+ console.log(topBorder);
549
+ if (title) {
550
+ const titleText = bold(title);
551
+ const titlePadding = totalWidth - title.length - 1;
552
+ const titleRow = "\u2502 " + titleText + " ".repeat(titlePadding) + "\u2502";
553
+ console.log(titleRow);
554
+ console.log(titleSeparator);
535
555
  }
536
- });
537
-
538
- // src/constants.ts
539
- var init_constants2 = __esm({
540
- "src/constants.ts"() {
541
- "use strict";
542
- init_constants();
556
+ const header = "\u2502 " + keys.map((key, i) => key.padEnd(widths[i])).join(" \u2502 ") + " \u2502";
557
+ console.log(header);
558
+ console.log(separator);
559
+ rows.forEach((row) => {
560
+ const dataRow = "\u2502 " + row.map((cell, i) => cell.padEnd(widths[i])).join(" \u2502 ") + " \u2502";
561
+ console.log(dataRow);
562
+ });
563
+ console.log(bottomBorder);
564
+ }
565
+ var logger = {
566
+ table: (data, title) => {
567
+ formatTable(data, title);
568
+ },
569
+ /**
570
+ * Info message - general information
571
+ */
572
+ info: (message, indent = 0) => {
573
+ const spaces = " ".repeat(indent);
574
+ console.log(`${spaces}${blue("\u2139")} ${bold(customTransform(message))}`);
575
+ },
576
+ /**
577
+ * Admonition - highlighted note/tip/warning box (Docusaurus-style)
578
+ */
579
+ admonition: (type, title, lines, indent = 0) => {
580
+ const spaces = " ".repeat(indent);
581
+ const configs = {
582
+ note: { color: green },
583
+ tip: { color: cyan },
584
+ info: { color: blue },
585
+ warning: { color: yellow }
586
+ };
587
+ const { color } = configs[type];
588
+ console.log(`${spaces}${color("\u250C\u2500")} ${bold(color(title.toUpperCase()))}`);
589
+ if (lines && lines.length > 0) {
590
+ lines.forEach((line) => {
591
+ console.log(`${spaces}${color("\u2502")} ${customTransform(line)}`);
592
+ });
593
+ }
594
+ console.log(`${spaces}${color("\u2514\u2500")}`);
595
+ },
596
+ /**
597
+ * Dim message - less important information
598
+ */
599
+ dim: (message, indent = 0) => {
600
+ const spaces = " ".repeat(indent);
601
+ console.log(`${spaces}${dim(customTransform(message))}`);
602
+ },
603
+ /**
604
+ * Success message - operation completed successfully
605
+ */
606
+ success: (message, indent = 0) => {
607
+ const spaces = " ".repeat(indent);
608
+ console.log(`${spaces}${green("\u2714")} ${bold(customTransform(message))}`);
609
+ },
610
+ remark: (message, indent = 0) => {
611
+ const spaces = " ".repeat(indent);
612
+ console.log(`${spaces}${bold(yellowBright("\u2726"))} ${bold(customTransform(message))}`);
613
+ },
614
+ /**
615
+ * Error message - operation failed
616
+ */
617
+ error: (message, indent = 0) => {
618
+ const spaces = " ".repeat(indent);
619
+ console.error(`${spaces}${red("\u2716")} ${customTransform(message)}`);
620
+ },
621
+ bold: (message, indent = 0) => {
622
+ const spaces = " ".repeat(indent);
623
+ console.log(`${spaces}${bold(customTransform(message))}`);
624
+ },
625
+ /**
626
+ * Warning message - something to be aware of
627
+ */
628
+ warn: (message, indent = 0) => {
629
+ const spaces = " ".repeat(indent);
630
+ console.warn(`${spaces}${yellow("\u26A0")} ${bold(customTransform(message))}`);
631
+ },
632
+ /**
633
+ * Debug message - only shown when DEBUG env var is set
634
+ */
635
+ debug: (message, indent = 0) => {
636
+ const spaces = " ".repeat(indent);
637
+ if (process.env.DEBUG) {
638
+ console.log(gray("[DEBUG]"), `${spaces}${message}`);
639
+ }
640
+ },
641
+ /**
642
+ * Step message - shows progress through a process
643
+ */
644
+ step: (step, total, message, indent = 0) => {
645
+ const spaces = " ".repeat(indent);
646
+ console.log(spaces + cyan(`[${step}/${total}]`), customTransform(message));
647
+ },
648
+ /**
649
+ * Highlighted message - draws attention
650
+ */
651
+ highlight: (message, indent = 0) => {
652
+ const spaces = " ".repeat(indent);
653
+ console.log(bold(`${spaces}${cyan(customTransform(message))}`));
654
+ },
655
+ /**
656
+ * Aside message - for side information
657
+ */
658
+ aside: (message, indent = 0) => {
659
+ const spaces = " ".repeat(indent);
660
+ console.log(`${spaces}${bold(customTransform(message))}`);
661
+ },
662
+ /**
663
+ * Data display - for structured data output
664
+ */
665
+ data: (label, value, indent = 0) => {
666
+ const spaces = " ".repeat(indent);
667
+ if (value !== void 0) {
668
+ console.log(`${spaces}${dim(label + ":")} ${bold(value)}`);
669
+ } else {
670
+ console.log(`${spaces}${dim(label)}`);
671
+ }
672
+ },
673
+ /**
674
+ * JSON output - pretty-printed JSON
675
+ */
676
+ json: (data, indent = 0) => {
677
+ const spaces = " ".repeat(indent);
678
+ const jsonString = colorize(JSON.stringify(data, null, 2));
679
+ jsonString.split("\n").forEach((line) => {
680
+ console.log(`${spaces}${line}`);
681
+ });
682
+ },
683
+ /**
684
+ * New line
685
+ */
686
+ newLine: () => {
687
+ console.log();
688
+ },
689
+ /**
690
+ * Raw output - no formatting, useful for ASCII art or pre-formatted text
691
+ */
692
+ raw: (text, indent = 0) => {
693
+ const spaces = " ".repeat(indent);
694
+ console.log(`${spaces}${text}`);
695
+ },
696
+ customRaw: (text, indent = 0) => {
697
+ const spaces = " ".repeat(indent);
698
+ console.log(`${spaces}${customTransform(text)}`);
699
+ },
700
+ /**
701
+ * Display a configuration error with helpful suggestions
702
+ */
703
+ configError: (error, indent = 0) => {
704
+ const spaces = " ".repeat(indent);
705
+ const isConfigError = error && typeof error === "object" && "name" in error && error.name === "ConfigError";
706
+ if (isConfigError && "message" in error && "field" in error && "suggestion" in error) {
707
+ const configErr = error;
708
+ console.error(`${spaces}${red("\u2716")} ${bold(configErr.message)}`);
709
+ if (configErr.field) {
710
+ console.error(`${spaces} ${dim("Field:")} ${configErr.field}`);
711
+ }
712
+ if (configErr.suggestion) {
713
+ console.error(`${spaces} ${dim("Fix:")} ${configErr.suggestion}`);
714
+ }
715
+ } else if (error instanceof Error) {
716
+ console.error(`${spaces}${red("\u2716")} ${bold(error.message)}`);
717
+ } else {
718
+ console.error(`${spaces}${red("\u2716")} ${bold(String(error))}`);
719
+ }
543
720
  }
544
- });
721
+ };
545
722
 
546
723
  // src/lib/config/loader.ts
547
- import { dirname as dirname2, resolve as resolve2 } from "path";
724
+ var ConfigError = class extends Error {
725
+ constructor(message, field, suggestion) {
726
+ super(message);
727
+ this.field = field;
728
+ this.suggestion = suggestion;
729
+ this.name = "ConfigError";
730
+ }
731
+ /**
732
+ * Format a user-friendly error message with the field and suggestion
733
+ */
734
+ toString() {
735
+ let msg = `Configuration Error: ${this.message}`;
736
+ if (this.field) {
737
+ msg += `
738
+ Field: ${this.field}`;
739
+ }
740
+ if (this.suggestion) {
741
+ msg += `
742
+ Suggestion: ${this.suggestion}`;
743
+ }
744
+ return msg;
745
+ }
746
+ };
548
747
  async function findConfigPath(configPath) {
549
748
  const { findFile: findFile2 } = await Promise.resolve().then(() => (init_file_loader(), file_loader_exports));
550
749
  if (configPath) {
551
750
  const fullPath = resolve2(configPath);
552
- const result2 = await findFile2(fullPath, { cwd: dirname2(fullPath) });
751
+ const result2 = await findFile2(fullPath, { cwd: dirname2(fullPath), searchUp: false });
553
752
  if (!result2) {
554
753
  throw new ConfigError(
555
754
  `Config file not found: ${fullPath}`,
@@ -572,10 +771,7 @@ async function findConfigPath(configPath) {
572
771
  }
573
772
  return result2.path;
574
773
  }
575
- const result = await findFile2(CONFIG_FILE_NAMES, {
576
- searchUp: true,
577
- maxLevels: 10
578
- });
774
+ const result = await findFile2(CONFIG_FILE_NAMES);
579
775
  if (!result) {
580
776
  throw new ConfigError(
581
777
  "No Playcademy config file found in this directory or any parent directory",
@@ -720,642 +916,15 @@ function processConfigVariables(config) {
720
916
  const result = {};
721
917
  for (const [key, val] of Object.entries(obj)) {
722
918
  result[key] = processValue(val);
723
- }
724
- return result;
725
- }
726
- return value;
727
- };
728
- return processValue(processed);
729
- }
730
- var ConfigError;
731
- var init_loader = __esm({
732
- "src/lib/config/loader.ts"() {
733
- "use strict";
734
- init_file_loader();
735
- init_constants2();
736
- ConfigError = class extends Error {
737
- constructor(message, field, suggestion) {
738
- super(message);
739
- this.field = field;
740
- this.suggestion = suggestion;
741
- this.name = "ConfigError";
742
- }
743
- /**
744
- * Format a user-friendly error message with the field and suggestion
745
- */
746
- toString() {
747
- let msg = `Configuration Error: ${this.message}`;
748
- if (this.field) {
749
- msg += `
750
- Field: ${this.field}`;
751
- }
752
- if (this.suggestion) {
753
- msg += `
754
- Suggestion: ${this.suggestion}`;
755
- }
756
- return msg;
757
- }
758
- };
759
- }
760
- });
761
-
762
- // ../utils/src/package-manager.ts
763
- import { execSync } from "child_process";
764
- import { existsSync as existsSync3 } from "fs";
765
- import { join as join3 } from "path";
766
- function isCommandAvailable(command) {
767
- try {
768
- execSync(`command -v ${command}`, { stdio: "ignore" });
769
- return true;
770
- } catch {
771
- return false;
772
- }
773
- }
774
- function detectPackageManager(cwd = process.cwd()) {
775
- if (existsSync3(join3(cwd, "bun.lock")) || existsSync3(join3(cwd, "bun.lockb"))) {
776
- return "bun";
777
- }
778
- if (existsSync3(join3(cwd, "pnpm-lock.yaml"))) {
779
- return "pnpm";
780
- }
781
- if (existsSync3(join3(cwd, "yarn.lock"))) {
782
- return "yarn";
783
- }
784
- if (existsSync3(join3(cwd, "package-lock.json"))) {
785
- return "npm";
786
- }
787
- return detectByCommandAvailability();
788
- }
789
- function detectByCommandAvailability() {
790
- if (isCommandAvailable("bun")) {
791
- return "bun";
792
- }
793
- if (isCommandAvailable("pnpm")) {
794
- return "pnpm";
795
- }
796
- if (isCommandAvailable("yarn")) {
797
- return "yarn";
798
- }
799
- return "npm";
800
- }
801
- function getInstallCommand(pm) {
802
- switch (pm) {
803
- case "bun":
804
- return "bun install";
805
- case "pnpm":
806
- return "pnpm install";
807
- case "yarn":
808
- return "yarn install";
809
- case "npm":
810
- default:
811
- return "npm install";
812
- }
813
- }
814
- var init_package_manager = __esm({
815
- "../utils/src/package-manager.ts"() {
816
- "use strict";
817
- }
818
- });
819
-
820
- // src/lib/core/context.ts
821
- function getWorkspace() {
822
- return context.workspace || process.cwd();
823
- }
824
- var context;
825
- var init_context = __esm({
826
- "src/lib/core/context.ts"() {
827
- "use strict";
828
- init_package_manager();
829
- context = {};
830
- }
831
- });
832
-
833
- // src/lib/core/logger.ts
834
- import {
835
- blue,
836
- blueBright,
837
- bold,
838
- cyan,
839
- dim,
840
- gray,
841
- green,
842
- greenBright,
843
- red,
844
- yellow,
845
- yellowBright
846
- } from "colorette";
847
- import { colorize } from "json-colorizer";
848
- function customTransform(text) {
849
- let result = text;
850
- result = result.replace(/`([^`]+)`/g, (_, code) => greenBright(code));
851
- result = result.replace(/<([^>]+)>/g, (_, path2) => blueBright(path2));
852
- return result;
853
- }
854
- function formatTable(data, title) {
855
- if (data.length === 0) return;
856
- const keys = Object.keys(data[0]);
857
- const rows = data.map((item) => keys.map((key) => String(item[key] ?? "")));
858
- const widths = keys.map((key, i) => {
859
- const headerWidth = key.length;
860
- const dataWidth = Math.max(...rows.map((row) => row[i].length));
861
- return Math.max(headerWidth, dataWidth);
862
- });
863
- const totalWidth = widths.reduce((sum, w) => sum + w + 3, -1);
864
- const separator = "\u251C" + widths.map((w) => "\u2500".repeat(w + 2)).join("\u253C") + "\u2524";
865
- const topBorder = "\u250C" + "\u2500".repeat(totalWidth) + "\u2510";
866
- const titleSeparator = "\u251C" + widths.map((w) => "\u2500".repeat(w + 2)).join("\u252C") + "\u2524";
867
- const bottomBorder = "\u2514" + widths.map((w) => "\u2500".repeat(w + 2)).join("\u2534") + "\u2518";
868
- console.log(topBorder);
869
- if (title) {
870
- const titleText = bold(title);
871
- const titlePadding = totalWidth - title.length - 1;
872
- const titleRow = "\u2502 " + titleText + " ".repeat(titlePadding) + "\u2502";
873
- console.log(titleRow);
874
- console.log(titleSeparator);
875
- }
876
- const header = "\u2502 " + keys.map((key, i) => key.padEnd(widths[i])).join(" \u2502 ") + " \u2502";
877
- console.log(header);
878
- console.log(separator);
879
- rows.forEach((row) => {
880
- const dataRow = "\u2502 " + row.map((cell, i) => cell.padEnd(widths[i])).join(" \u2502 ") + " \u2502";
881
- console.log(dataRow);
882
- });
883
- console.log(bottomBorder);
884
- }
885
- var logger;
886
- var init_logger = __esm({
887
- "src/lib/core/logger.ts"() {
888
- "use strict";
889
- logger = {
890
- table: (data, title) => {
891
- formatTable(data, title);
892
- },
893
- /**
894
- * Info message - general information
895
- */
896
- info: (message, indent = 0) => {
897
- const spaces = " ".repeat(indent);
898
- console.log(`${spaces}${blue("\u2139")} ${bold(customTransform(message))}`);
899
- },
900
- /**
901
- * Admonition - highlighted note/tip/warning box (Docusaurus-style)
902
- */
903
- admonition: (type, title, lines, indent = 0) => {
904
- const spaces = " ".repeat(indent);
905
- const configs = {
906
- note: { color: green },
907
- tip: { color: cyan },
908
- info: { color: blue },
909
- warning: { color: yellow }
910
- };
911
- const { color } = configs[type];
912
- console.log(`${spaces}${color("\u250C\u2500")} ${bold(color(title.toUpperCase()))}`);
913
- if (lines && lines.length > 0) {
914
- lines.forEach((line) => {
915
- console.log(`${spaces}${color("\u2502")} ${customTransform(line)}`);
916
- });
917
- }
918
- console.log(`${spaces}${color("\u2514\u2500")}`);
919
- },
920
- /**
921
- * Dim message - less important information
922
- */
923
- dim: (message, indent = 0) => {
924
- const spaces = " ".repeat(indent);
925
- console.log(`${spaces}${dim(customTransform(message))}`);
926
- },
927
- /**
928
- * Success message - operation completed successfully
929
- */
930
- success: (message, indent = 0) => {
931
- const spaces = " ".repeat(indent);
932
- console.log(`${spaces}${green("\u2714")} ${bold(customTransform(message))}`);
933
- },
934
- remark: (message, indent = 0) => {
935
- const spaces = " ".repeat(indent);
936
- console.log(`${spaces}${bold(yellowBright("\u2726"))} ${bold(customTransform(message))}`);
937
- },
938
- /**
939
- * Error message - operation failed
940
- */
941
- error: (message, indent = 0) => {
942
- const spaces = " ".repeat(indent);
943
- console.error(`${spaces}${red("\u2716")} ${customTransform(message)}`);
944
- },
945
- bold: (message, indent = 0) => {
946
- const spaces = " ".repeat(indent);
947
- console.log(`${spaces}${bold(customTransform(message))}`);
948
- },
949
- /**
950
- * Warning message - something to be aware of
951
- */
952
- warn: (message, indent = 0) => {
953
- const spaces = " ".repeat(indent);
954
- console.warn(`${spaces}${yellow("\u26A0")} ${bold(customTransform(message))}`);
955
- },
956
- /**
957
- * Debug message - only shown when DEBUG env var is set
958
- */
959
- debug: (message, indent = 0) => {
960
- const spaces = " ".repeat(indent);
961
- if (process.env.DEBUG) {
962
- console.log(gray("[DEBUG]"), `${spaces}${message}`);
963
- }
964
- },
965
- /**
966
- * Step message - shows progress through a process
967
- */
968
- step: (step, total, message, indent = 0) => {
969
- const spaces = " ".repeat(indent);
970
- console.log(spaces + cyan(`[${step}/${total}]`), customTransform(message));
971
- },
972
- /**
973
- * Highlighted message - draws attention
974
- */
975
- highlight: (message, indent = 0) => {
976
- const spaces = " ".repeat(indent);
977
- console.log(bold(`${spaces}${cyan(customTransform(message))}`));
978
- },
979
- /**
980
- * Aside message - for side information
981
- */
982
- aside: (message, indent = 0) => {
983
- const spaces = " ".repeat(indent);
984
- console.log(`${spaces}${bold(customTransform(message))}`);
985
- },
986
- /**
987
- * Data display - for structured data output
988
- */
989
- data: (label, value, indent = 0) => {
990
- const spaces = " ".repeat(indent);
991
- if (value !== void 0) {
992
- console.log(`${spaces}${dim(label + ":")} ${bold(value)}`);
993
- } else {
994
- console.log(`${spaces}${dim(label)}`);
995
- }
996
- },
997
- /**
998
- * JSON output - pretty-printed JSON
999
- */
1000
- json: (data, indent = 0) => {
1001
- const spaces = " ".repeat(indent);
1002
- const jsonString = colorize(JSON.stringify(data, null, 2));
1003
- jsonString.split("\n").forEach((line) => {
1004
- console.log(`${spaces}${line}`);
1005
- });
1006
- },
1007
- /**
1008
- * New line
1009
- */
1010
- newLine: () => {
1011
- console.log();
1012
- },
1013
- /**
1014
- * Raw output - no formatting, useful for ASCII art or pre-formatted text
1015
- */
1016
- raw: (text, indent = 0) => {
1017
- const spaces = " ".repeat(indent);
1018
- console.log(`${spaces}${text}`);
1019
- },
1020
- customRaw: (text, indent = 0) => {
1021
- const spaces = " ".repeat(indent);
1022
- console.log(`${spaces}${customTransform(text)}`);
1023
- },
1024
- /**
1025
- * Display a configuration error with helpful suggestions
1026
- */
1027
- configError: (error, indent = 0) => {
1028
- const spaces = " ".repeat(indent);
1029
- const isConfigError = error && typeof error === "object" && "name" in error && error.name === "ConfigError";
1030
- if (isConfigError && "message" in error && "field" in error && "suggestion" in error) {
1031
- const configErr = error;
1032
- console.error(`${spaces}${red("\u2716")} ${bold(configErr.message)}`);
1033
- if (configErr.field) {
1034
- console.error(`${spaces} ${dim("Field:")} ${configErr.field}`);
1035
- }
1036
- if (configErr.suggestion) {
1037
- console.error(`${spaces} ${dim("Fix:")} ${configErr.suggestion}`);
1038
- }
1039
- } else if (error instanceof Error) {
1040
- console.error(`${spaces}${red("\u2716")} ${bold(error.message)}`);
1041
- } else {
1042
- console.error(`${spaces}${red("\u2716")} ${bold(String(error))}`);
1043
- }
1044
- }
1045
- };
1046
- }
1047
- });
1048
-
1049
- // src/lib/core/config.ts
1050
- var init_config2 = __esm({
1051
- "src/lib/core/config.ts"() {
1052
- "use strict";
1053
- init_constants2();
1054
- init_context();
1055
- init_logger();
1056
- }
1057
- });
1058
-
1059
- // src/lib/auth/storage.ts
1060
- var init_storage = __esm({
1061
- "src/lib/auth/storage.ts"() {
1062
- "use strict";
1063
- init_constants2();
1064
- init_config2();
1065
- }
1066
- });
1067
-
1068
- // src/lib/core/client.ts
1069
- import { PlaycademyClient } from "@playcademy/sdk";
1070
- var init_client = __esm({
1071
- "src/lib/core/client.ts"() {
1072
- "use strict";
1073
- init_storage();
1074
- init_logger();
1075
- init_config2();
1076
- }
1077
- });
1078
-
1079
- // src/lib/core/errors.ts
1080
- function getErrorMessage(error) {
1081
- if (error instanceof Error) return error.message;
1082
- if (typeof error === "string") return error;
1083
- if (error && typeof error === "object" && "message" in error) {
1084
- return String(error.message);
1085
- }
1086
- return "Unknown error";
1087
- }
1088
- var init_errors = __esm({
1089
- "src/lib/core/errors.ts"() {
1090
- "use strict";
1091
- }
1092
- });
1093
-
1094
- // ../utils/src/uuid.ts
1095
- var init_uuid = __esm({
1096
- "../utils/src/uuid.ts"() {
1097
- "use strict";
1098
- }
1099
- });
1100
-
1101
- // ../utils/src/mime.ts
1102
- var init_mime = __esm({
1103
- "../utils/src/mime.ts"() {
1104
- "use strict";
1105
- }
1106
- });
1107
-
1108
- // ../utils/src/ansi.ts
1109
- var isInteractive;
1110
- var init_ansi = __esm({
1111
- "../utils/src/ansi.ts"() {
1112
- "use strict";
1113
- isInteractive = typeof process !== "undefined" && process.stdout?.isTTY && !process.env.CI && process.env.TERM !== "dumb";
1114
- }
1115
- });
1116
-
1117
- // ../utils/src/spinner.ts
1118
- var SPINNER_FRAMES, CHECK_MARK, CROSS_MARK;
1119
- var init_spinner = __esm({
1120
- "../utils/src/spinner.ts"() {
1121
- "use strict";
1122
- init_ansi();
1123
- SPINNER_FRAMES = [
1124
- 10251,
1125
- 10265,
1126
- 10297,
1127
- 10296,
1128
- 10300,
1129
- 10292,
1130
- 10278,
1131
- 10279,
1132
- 10247,
1133
- 10255
1134
- ].map((code) => String.fromCodePoint(code));
1135
- CHECK_MARK = String.fromCodePoint(10004);
1136
- CROSS_MARK = String.fromCodePoint(10006);
1137
- }
1138
- });
1139
-
1140
- // ../utils/src/log.ts
1141
- var init_log = __esm({
1142
- "../utils/src/log.ts"() {
1143
- "use strict";
1144
- init_ansi();
1145
- init_spinner();
1146
- }
1147
- });
1148
-
1149
- // ../utils/src/name.ts
1150
- var init_name = __esm({
1151
- "../utils/src/name.ts"() {
1152
- "use strict";
1153
- }
1154
- });
1155
-
1156
- // ../utils/src/string.ts
1157
- function pluralize(count, singular, plural) {
1158
- return count === 1 ? singular : plural || `${singular}s`;
1159
- }
1160
- var init_string = __esm({
1161
- "../utils/src/string.ts"() {
1162
- "use strict";
1163
- }
1164
- });
1165
-
1166
- // ../utils/src/lifecycle.ts
1167
- var init_lifecycle = __esm({
1168
- "../utils/src/lifecycle.ts"() {
1169
- "use strict";
1170
- }
1171
- });
1172
-
1173
- // ../utils/src/timezone.ts
1174
- var init_timezone = __esm({
1175
- "../utils/src/timezone.ts"() {
1176
- "use strict";
1177
- }
1178
- });
1179
-
1180
- // ../utils/src/slug.ts
1181
- var init_slug = __esm({
1182
- "../utils/src/slug.ts"() {
1183
- "use strict";
1184
- }
1185
- });
1186
-
1187
- // ../utils/src/pure/index.ts
1188
- var init_pure = __esm({
1189
- "../utils/src/pure/index.ts"() {
1190
- "use strict";
1191
- init_uuid();
1192
- init_mime();
1193
- init_log();
1194
- init_name();
1195
- init_string();
1196
- init_lifecycle();
1197
- init_timezone();
1198
- init_package_json();
1199
- init_slug();
1200
- }
1201
- });
1202
-
1203
- // ../utils/src/index.ts
1204
- var init_src2 = __esm({
1205
- "../utils/src/index.ts"() {
1206
- "use strict";
1207
- init_pure();
1208
- }
1209
- });
1210
-
1211
- // src/lib/config/timeback-derive.ts
1212
- var init_timeback_derive = __esm({
1213
- "src/lib/config/timeback-derive.ts"() {
1214
- "use strict";
1215
- init_game();
1216
- }
1217
- });
1218
-
1219
- // src/lib/templates/loader.ts
1220
- import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
1221
- import { dirname as dirname3, resolve as resolve3 } from "path";
1222
- import { fileURLToPath } from "url";
1223
- function loadTemplateString(filename) {
1224
- const filenamesToTry = filename.endsWith(".template") ? [filename] : [filename, `${filename}.template`];
1225
- const candidatePaths = filenamesToTry.flatMap((name) => [
1226
- // Dev (TS sources): ../../templates from src/lib/templates
1227
- resolve3(currentDir, "../../templates", name),
1228
- // Bundled build (single-file output): templates relative to dist root
1229
- resolve3(currentDir, "templates", name)
1230
- ]);
1231
- for (const candidate of candidatePaths) {
1232
- if (existsSync4(candidate)) {
1233
- return readFileSync2(candidate, "utf-8");
1234
- }
1235
- }
1236
- throw new Error(`Template not found: ${filename}. Searched: ${candidatePaths.join(", ")}`);
1237
- }
1238
- var currentDir;
1239
- var init_loader2 = __esm({
1240
- "src/lib/templates/loader.ts"() {
1241
- "use strict";
1242
- currentDir = dirname3(fileURLToPath(import.meta.url));
1243
- }
1244
- });
1245
-
1246
- // src/lib/config/generator.ts
1247
- var init_generator = __esm({
1248
- "src/lib/config/generator.ts"() {
1249
- "use strict";
1250
- init_loader2();
1251
- }
1252
- });
1253
-
1254
- // src/lib/config/writer.ts
1255
- var init_writer = __esm({
1256
- "src/lib/config/writer.ts"() {
1257
- "use strict";
1258
- }
1259
- });
1260
-
1261
- // src/lib/config/index.ts
1262
- var init_config3 = __esm({
1263
- "src/lib/config/index.ts"() {
1264
- "use strict";
1265
- init_loader();
1266
- init_timeback_derive();
1267
- init_generator();
1268
- init_writer();
1269
- }
1270
- });
1271
-
1272
- // src/lib/core/game.ts
1273
- var init_game = __esm({
1274
- "src/lib/core/game.ts"() {
1275
- "use strict";
1276
- init_src2();
1277
- init_slug();
1278
- init_config3();
1279
- }
1280
- });
1281
-
1282
- // src/lib/core/gitignore.ts
1283
- var init_gitignore = __esm({
1284
- "src/lib/core/gitignore.ts"() {
1285
- "use strict";
1286
- }
1287
- });
1288
-
1289
- // src/lib/core/import.ts
1290
- import { mkdtempSync, rmSync } from "fs";
1291
- import { tmpdir } from "os";
1292
- import { join as join4 } from "path";
1293
- import { pathToFileURL } from "url";
1294
- import * as esbuild from "esbuild";
1295
- async function importTypescriptFile(filePath, bundleOptions) {
1296
- const tempDir = mkdtempSync(join4(tmpdir(), "playcademy-import-"));
1297
- const outFile = join4(tempDir, "bundle.mjs");
1298
- try {
1299
- await esbuild.build({
1300
- entryPoints: [filePath],
1301
- outfile: outFile,
1302
- bundle: true,
1303
- platform: "node",
1304
- format: "esm",
1305
- target: "node20",
1306
- sourcemap: false,
1307
- minify: false,
1308
- ...bundleOptions
1309
- });
1310
- const module = await import(pathToFileURL(outFile).href);
1311
- return module;
1312
- } finally {
1313
- rmSync(tempDir, { recursive: true, force: true });
1314
- }
1315
- }
1316
- async function importTypescriptDefault(filePath, bundleOptions) {
1317
- const module = await importTypescriptFile(filePath, bundleOptions);
1318
- if (module && typeof module === "object" && "default" in module) {
1319
- return module.default;
1320
- }
1321
- return module;
1322
- }
1323
- var init_import = __esm({
1324
- "src/lib/core/import.ts"() {
1325
- "use strict";
1326
- }
1327
- });
1328
-
1329
- // src/lib/core/mime.ts
1330
- var init_mime2 = __esm({
1331
- "src/lib/core/mime.ts"() {
1332
- "use strict";
1333
- }
1334
- });
1335
-
1336
- // src/lib/core/index.ts
1337
- var init_core = __esm({
1338
- "src/lib/core/index.ts"() {
1339
- "use strict";
1340
- init_client();
1341
- init_config2();
1342
- init_context();
1343
- init_errors();
1344
- init_game();
1345
- init_gitignore();
1346
- init_import();
1347
- init_logger();
1348
- init_mime2();
1349
- }
1350
- });
1351
-
1352
- // src/utils.ts
1353
- init_loader();
1354
- init_loader();
1355
- init_loader();
919
+ }
920
+ return result;
921
+ }
922
+ return value;
923
+ };
924
+ return processValue(processed);
925
+ }
1356
926
 
1357
927
  // src/lib/dev/server.ts
1358
- init_src();
1359
928
  import { mkdir as mkdir2 } from "fs/promises";
1360
929
  import { join as join12 } from "path";
1361
930
  import { Log, LogLevel, Miniflare } from "miniflare";
@@ -1447,17 +1016,212 @@ function readServerInfo(type, projectRoot) {
1447
1016
  return servers[0] || null;
1448
1017
  }
1449
1018
 
1450
- // src/lib/dev/server.ts
1451
- init_constants2();
1452
- init_loader();
1453
- init_core();
1019
+ // src/lib/core/client.ts
1020
+ import { PlaycademyClient } from "@playcademy/sdk";
1021
+
1022
+ // ../utils/src/package-manager.ts
1023
+ import { execSync } from "child_process";
1024
+ import { existsSync as existsSync3 } from "fs";
1025
+ import { join as join3 } from "path";
1026
+ function isCommandAvailable(command) {
1027
+ try {
1028
+ execSync(`command -v ${command}`, { stdio: "ignore" });
1029
+ return true;
1030
+ } catch {
1031
+ return false;
1032
+ }
1033
+ }
1034
+ function detectPackageManager(cwd = process.cwd()) {
1035
+ if (existsSync3(join3(cwd, "bun.lock")) || existsSync3(join3(cwd, "bun.lockb"))) {
1036
+ return "bun";
1037
+ }
1038
+ if (existsSync3(join3(cwd, "pnpm-lock.yaml"))) {
1039
+ return "pnpm";
1040
+ }
1041
+ if (existsSync3(join3(cwd, "yarn.lock"))) {
1042
+ return "yarn";
1043
+ }
1044
+ if (existsSync3(join3(cwd, "package-lock.json"))) {
1045
+ return "npm";
1046
+ }
1047
+ return detectByCommandAvailability();
1048
+ }
1049
+ function detectByCommandAvailability() {
1050
+ if (isCommandAvailable("bun")) {
1051
+ return "bun";
1052
+ }
1053
+ if (isCommandAvailable("pnpm")) {
1054
+ return "pnpm";
1055
+ }
1056
+ if (isCommandAvailable("yarn")) {
1057
+ return "yarn";
1058
+ }
1059
+ return "npm";
1060
+ }
1061
+ function getInstallCommand(pm) {
1062
+ switch (pm) {
1063
+ case "bun":
1064
+ return "bun install";
1065
+ case "pnpm":
1066
+ return "pnpm install";
1067
+ case "yarn":
1068
+ return "yarn install";
1069
+ case "npm":
1070
+ default:
1071
+ return "npm install";
1072
+ }
1073
+ }
1074
+
1075
+ // src/lib/core/context.ts
1076
+ var context = {};
1077
+ function getWorkspace() {
1078
+ return context.workspace || process.cwd();
1079
+ }
1080
+
1081
+ // src/lib/core/errors.ts
1082
+ function getErrorMessage(error) {
1083
+ if (error instanceof Error) return error.message;
1084
+ if (typeof error === "string") return error;
1085
+ if (error && typeof error === "object" && "message" in error) {
1086
+ return String(error.message);
1087
+ }
1088
+ return "Unknown error";
1089
+ }
1090
+
1091
+ // ../utils/src/ansi.ts
1092
+ var isInteractive = typeof process !== "undefined" && process.stdout?.isTTY && !process.env.CI && process.env.TERM !== "dumb";
1093
+
1094
+ // ../utils/src/spinner.ts
1095
+ var SPINNER_FRAMES = [
1096
+ 10251,
1097
+ 10265,
1098
+ 10297,
1099
+ 10296,
1100
+ 10300,
1101
+ 10292,
1102
+ 10278,
1103
+ 10279,
1104
+ 10247,
1105
+ 10255
1106
+ ].map((code) => String.fromCodePoint(code));
1107
+ var CHECK_MARK = String.fromCodePoint(10004);
1108
+ var CROSS_MARK = String.fromCodePoint(10006);
1109
+
1110
+ // ../utils/src/string.ts
1111
+ function pluralize(count, singular, plural) {
1112
+ return count === 1 ? singular : plural || `${singular}s`;
1113
+ }
1114
+
1115
+ // ../utils/src/pure/index.ts
1116
+ init_package_json();
1117
+
1118
+ // src/lib/secrets/env.ts
1119
+ init_file_loader();
1120
+ import { existsSync as existsSync4, writeFileSync as writeFileSync2 } from "fs";
1121
+ import { join as join4 } from "path";
1122
+ function parseEnvFile(contents) {
1123
+ const secrets = {};
1124
+ for (const line of contents.split("\n")) {
1125
+ const trimmed = line.trim();
1126
+ if (!trimmed || trimmed.startsWith("#")) {
1127
+ continue;
1128
+ }
1129
+ const match = trimmed.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
1130
+ if (match) {
1131
+ const [, key, value] = match;
1132
+ if (!key || !value) {
1133
+ continue;
1134
+ }
1135
+ const unquoted = value.replace(/^["']|["']$/g, "");
1136
+ secrets[key] = unquoted;
1137
+ }
1138
+ }
1139
+ return secrets;
1140
+ }
1141
+ async function readEnvFile(workspace) {
1142
+ let secrets = {};
1143
+ for (const filename of ENV_FILES) {
1144
+ try {
1145
+ const contents = await loadFile(filename, { cwd: workspace, searchUp: false });
1146
+ if (contents) {
1147
+ const fileSecrets = parseEnvFile(contents);
1148
+ secrets = { ...secrets, ...fileSecrets };
1149
+ }
1150
+ } catch {
1151
+ continue;
1152
+ }
1153
+ }
1154
+ return secrets;
1155
+ }
1156
+ function getLoadedEnvFiles(workspace) {
1157
+ return ENV_FILES.filter((filename) => existsSync4(join4(workspace, filename)));
1158
+ }
1159
+ function hasEnvFile(workspace) {
1160
+ return ENV_FILES.some((filename) => existsSync4(join4(workspace, filename)));
1161
+ }
1162
+
1163
+ // src/lib/templates/loader.ts
1164
+ import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
1165
+ import { dirname as dirname3, resolve as resolve3 } from "path";
1166
+ import { fileURLToPath } from "url";
1167
+ var currentDir = dirname3(fileURLToPath(import.meta.url));
1168
+ function loadTemplateString(filename) {
1169
+ const filenamesToTry = filename.endsWith(".template") ? [filename] : [filename, `${filename}.template`];
1170
+ const candidatePaths = filenamesToTry.flatMap((name) => [
1171
+ // Dev (TS sources): ../../templates from src/lib/templates
1172
+ resolve3(currentDir, "../../templates", name),
1173
+ // Bundled build (single-file output): templates relative to dist root
1174
+ resolve3(currentDir, "templates", name)
1175
+ ]);
1176
+ for (const candidate of candidatePaths) {
1177
+ if (existsSync5(candidate)) {
1178
+ return readFileSync2(candidate, "utf-8");
1179
+ }
1180
+ }
1181
+ throw new Error(`Template not found: ${filename}. Searched: ${candidatePaths.join(", ")}`);
1182
+ }
1183
+
1184
+ // src/lib/core/import.ts
1185
+ import { mkdtempSync, rmSync } from "fs";
1186
+ import { tmpdir } from "os";
1187
+ import { join as join5 } from "path";
1188
+ import { pathToFileURL } from "url";
1189
+ import * as esbuild from "esbuild";
1190
+ async function importTypescriptFile(filePath, bundleOptions) {
1191
+ const tempDir = mkdtempSync(join5(tmpdir(), "playcademy-import-"));
1192
+ const outFile = join5(tempDir, "bundle.mjs");
1193
+ try {
1194
+ await esbuild.build({
1195
+ entryPoints: [filePath],
1196
+ outfile: outFile,
1197
+ bundle: true,
1198
+ platform: "node",
1199
+ format: "esm",
1200
+ target: "node20",
1201
+ sourcemap: false,
1202
+ minify: false,
1203
+ ...bundleOptions
1204
+ });
1205
+ const module = await import(pathToFileURL(outFile).href);
1206
+ return module;
1207
+ } finally {
1208
+ rmSync(tempDir, { recursive: true, force: true });
1209
+ }
1210
+ }
1211
+ async function importTypescriptDefault(filePath, bundleOptions) {
1212
+ const module = await importTypescriptFile(filePath, bundleOptions);
1213
+ if (module && typeof module === "object" && "default" in module) {
1214
+ return module.default;
1215
+ }
1216
+ return module;
1217
+ }
1454
1218
 
1455
1219
  // src/lib/deploy/bundle.ts
1456
- import { existsSync as existsSync5 } from "fs";
1457
- import { join as join6 } from "path";
1220
+ import { existsSync as existsSync6 } from "fs";
1221
+ import { join as join7 } from "path";
1458
1222
 
1459
1223
  // ../edge-play/src/entry.ts
1460
- 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'\n\nimport { registerCors, registerEnvSetup, registerSdkInit } from './entry/middleware'\nimport { setupProcessGlobal } from './entry/setup'\nimport { registerBuiltinRoutes } from './register-routes'\n\nimport type { RuntimeConfig } from './entry/types'\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: RuntimeConfig\n\n// Setup process global polyfill for SDK compatibility\nsetupProcessGlobal()\n\n// Create Hono app\nconst app = new Hono<HonoEnv>()\n\n// Register middleware\nregisterCors(app)\nregisterEnvSetup(app, PLAYCADEMY_CONFIG)\nregisterSdkInit(app, PLAYCADEMY_CONFIG)\n\n// Register built-in integration routes based on enabled integrations\n// This function conditionally imports and registers routes like:\n// - GET /api (always included)\n// - GET /api/health (always included)\n// - POST /api/integrations/timeback/end-activity (if timeback enabled)\n//\n// Uses dynamic imports for tree-shaking: if an integration is not enabled,\n// its route code is completely removed from the bundle.\nawait registerBuiltinRoutes(app, PLAYCADEMY_CONFIG.integrations)\n\nexport default app\n";
1224
+ 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 * DO NOT REMOVE any code wrapped by \u26A0\uFE0F BUILD_MARKER: <marker> \u26A0\uFE0F\n */\n\nimport { Hono } from 'hono'\n\nimport {\n registerApiNotFoundHandler,\n registerAssetFallback,\n registerCors,\n registerEnvSetup,\n registerPlaycademyUser,\n registerSdkInit,\n} from './entry/middleware'\nimport { setupProcessGlobal } from './entry/setup'\nimport { registerBuiltinRoutes } from './register-routes'\n\nimport type { RuntimeConfig } from './entry/types'\nimport type { HonoEnv } from './types'\n\n// DO NOT REMOVE THE BELOW COMMENT\n// \u26A0\uFE0F BUILD_MARKER: CUSTOM_ROUTE_IMPORTS \u26A0\uFE0F\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: RuntimeConfig\n\n// Setup process global polyfill for SDK compatibility\nsetupProcessGlobal()\n\n// Create Hono app\nconst app = new Hono<HonoEnv>()\n\n// Register middleware\nregisterCors(app)\nregisterEnvSetup(app, PLAYCADEMY_CONFIG)\nregisterSdkInit(app, PLAYCADEMY_CONFIG)\nregisterPlaycademyUser(app)\n\n// DO NOT REMOVE THE BELOW COMMENT\n// \u26A0\uFE0F BUILD_MARKER: SESSION_MIDDLEWARE \u26A0\uFE0F\n\n// Register built-in integration routes based on enabled integrations\n// This function conditionally imports and registers routes like:\n// - GET /api (always included)\n// - GET /api/health (always included)\n// - POST /api/integrations/timeback/end-activity (if timeback enabled)\n//\n// Uses dynamic imports for tree-shaking: if an integration is not enabled,\n// its route code is completely removed from the bundle.\nawait registerBuiltinRoutes(app, PLAYCADEMY_CONFIG.integrations)\n\n// DO NOT REMOVE THE BELOW COMMENT\n// \u26A0\uFE0F BUILD_MARKER: CUSTOM_ROUTES \u26A0\uFE0F\n\n// Register API 404 handler\n// Returns JSON error for unmatched /api/* routes\n// Must be registered after all API routes\nregisterApiNotFoundHandler(app)\n\n// Register static asset fallback handler\n// Serves frontend assets from Workers Assets binding\n// MUST be registered last as it uses a wildcard GET route (app.get('*', ...))\n//\n// In production: Serves frontend assets from Workers Assets binding\n// In local dev: Returns 404 (Vite serves the frontend separately)\nregisterAssetFallback(app)\n\nexport default app\n";
1461
1225
 
1462
1226
  // ../utils/src/path.ts
1463
1227
  import fs from "node:fs";
@@ -1604,8 +1368,13 @@ var shouldLog = (level) => {
1604
1368
  const minLevel = getMinimumLogLevel();
1605
1369
  return levelPriority[level] >= levelPriority[minLevel];
1606
1370
  };
1371
+ var customHandler;
1607
1372
  var performLog = (level, message, context2) => {
1608
1373
  if (!shouldLog(level)) return;
1374
+ if (customHandler) {
1375
+ customHandler(level, message, context2);
1376
+ return;
1377
+ }
1609
1378
  const outputFormat = detectOutputFormat();
1610
1379
  switch (outputFormat) {
1611
1380
  case "browser":
@@ -1677,9 +1446,6 @@ function getMonorepoRoot() {
1677
1446
  return memoizedRoot;
1678
1447
  }
1679
1448
 
1680
- // src/lib/deploy/bundle.ts
1681
- init_constants2();
1682
-
1683
1449
  // src/lib/build/plugins.ts
1684
1450
  function textLoaderPlugin() {
1685
1451
  return {
@@ -1713,15 +1479,11 @@ function textLoaderPlugin() {
1713
1479
  };
1714
1480
  }
1715
1481
 
1716
- // src/lib/deploy/bundle.ts
1717
- init_core();
1718
-
1719
1482
  // src/lib/dev/routes.ts
1720
1483
  init_file_loader();
1721
- init_core();
1722
1484
  import { mkdir, writeFile } from "fs/promises";
1723
1485
  import { tmpdir as tmpdir2 } from "os";
1724
- import { join as join5, relative } from "path";
1486
+ import { join as join6, relative } from "path";
1725
1487
 
1726
1488
  // src/lib/deploy/hash.ts
1727
1489
  import { createHash } from "crypto";
@@ -1740,7 +1502,7 @@ async function discoverRoutes(apiDir) {
1740
1502
  const routes = await Promise.all(
1741
1503
  files.map(async (file) => {
1742
1504
  const routePath = filePathToRoutePath(file);
1743
- const absolutePath = join5(apiDir, file);
1505
+ const absolutePath = join6(apiDir, file);
1744
1506
  const relativePath = relative(getWorkspace(), absolutePath);
1745
1507
  const methods = await detectExportedMethods(absolutePath);
1746
1508
  return {
@@ -1801,10 +1563,10 @@ async function transpileRoute(filePath) {
1801
1563
  if (!result.outputFiles?.[0]) {
1802
1564
  throw new Error("Transpilation failed: no output");
1803
1565
  }
1804
- const tempDir = join5(tmpdir2(), "playcademy-dev");
1566
+ const tempDir = join6(tmpdir2(), "playcademy-dev");
1805
1567
  await mkdir(tempDir, { recursive: true });
1806
1568
  const hash = hashContent(filePath).slice(0, 12);
1807
- const jsPath = join5(tempDir, `${hash}.mjs`);
1569
+ const jsPath = join6(tempDir, `${hash}.mjs`);
1808
1570
  await writeFile(jsPath, result.outputFiles[0].text);
1809
1571
  return jsPath;
1810
1572
  }
@@ -1815,7 +1577,7 @@ async function discoverCustomRoutes(config) {
1815
1577
  const workspace = getWorkspace();
1816
1578
  const customRoutesConfig = config.integrations?.customRoutes;
1817
1579
  const customRoutesDir = typeof customRoutesConfig === "object" && customRoutesConfig.directory || DEFAULT_API_ROUTES_DIRECTORY;
1818
- const customRoutes = await discoverRoutes(join6(workspace, customRoutesDir));
1580
+ const customRoutes = await discoverRoutes(join7(workspace, customRoutesDir));
1819
1581
  const customRouteData = customRoutes.map((r) => ({
1820
1582
  path: r.path,
1821
1583
  file: r.file,
@@ -1827,15 +1589,15 @@ async function discoverCustomRoutes(config) {
1827
1589
  function resolveEmbeddedSourcePaths() {
1828
1590
  const workspace = getWorkspace();
1829
1591
  const distDir = new URL(".", import.meta.url).pathname;
1830
- const embeddedEdgeSrc = join6(distDir, "edge-play", "src");
1831
- const isBuiltPackage = existsSync5(embeddedEdgeSrc);
1592
+ const embeddedEdgeSrc = join7(distDir, "edge-play", "src");
1593
+ const isBuiltPackage = existsSync6(embeddedEdgeSrc);
1832
1594
  const monorepoRoot = getMonorepoRoot();
1833
- const monorepoEdgeSrc = join6(monorepoRoot, "packages/edge-play/src");
1595
+ const monorepoEdgeSrc = join7(monorepoRoot, "packages/edge-play/src");
1834
1596
  const edgePlaySrc = isBuiltPackage ? embeddedEdgeSrc : monorepoEdgeSrc;
1835
- const cliPackageRoot = isBuiltPackage ? join6(distDir, "../../..") : join6(monorepoRoot, "packages/cli");
1836
- const cliNodeModules = isBuiltPackage ? join6(cliPackageRoot, "node_modules") : monorepoRoot;
1837
- const workspaceNodeModules = join6(workspace, "node_modules");
1838
- const constantsEntry = isBuiltPackage ? join6(embeddedEdgeSrc, "..", "..", "constants", "src", "index.ts") : join6(monorepoRoot, "packages", "constants", "src", "index.ts");
1597
+ const cliPackageRoot = isBuiltPackage ? join7(distDir, "../../..") : join7(monorepoRoot, "packages/cli");
1598
+ const cliNodeModules = isBuiltPackage ? join7(cliPackageRoot, "node_modules") : monorepoRoot;
1599
+ const workspaceNodeModules = join7(workspace, "node_modules");
1600
+ const constantsEntry = isBuiltPackage ? join7(embeddedEdgeSrc, "..", "..", "constants", "src", "index.ts") : join7(monorepoRoot, "packages", "constants", "src", "index.ts");
1839
1601
  return {
1840
1602
  isBuiltPackage,
1841
1603
  edgePlaySrc,
@@ -1890,21 +1652,32 @@ function createEsbuildConfig(entryCode, paths, bundleConfig, customRoutesDir, op
1890
1652
  // │ install. We embed it in dist/ and alias imports to point there. │
1891
1653
  // └─────────────────────────────────────────────────────────────────┘
1892
1654
  "@playcademy/constants": constantsEntry,
1655
+ // ┌─ Workspace-only edge-play package ──────────────────────────────┐
1656
+ // │ @playcademy/edge-play is used in generated entry code for auth │
1657
+ // │ middleware. It's embedded in dist/ like constants. │
1658
+ // └─────────────────────────────────────────────────────────────────┘
1659
+ "@playcademy/edge-play": edgePlaySrc,
1893
1660
  // ┌─ User's custom routes ──────────────────────────────────────────┐
1894
1661
  // │ @game-api is a virtual module that maps to the user's API dir. │
1895
1662
  // │ Example: import * as route from '@game-api/hello.ts' │
1896
1663
  // │ Resolves to: /user-project/server/api/hello.ts │
1897
1664
  // └─────────────────────────────────────────────────────────────────┘
1898
- "@game-api": join6(workspace, customRoutesDir),
1665
+ "@game-api": join7(workspace, customRoutesDir),
1666
+ // ┌─ User's server lib directory ───────────────────────────────────┐
1667
+ // │ @game-server is a virtual module for server utilities/config │
1668
+ // │ Example: import { getAuth } from '@game-server/lib/auth' │
1669
+ // │ Resolves to: /user-project/server/lib/auth.ts │
1670
+ // └─────────────────────────────────────────────────────────────────┘
1671
+ "@game-server": join7(workspace, "server"),
1899
1672
  // ┌─ Node.js polyfills for Cloudflare Workers ──────────────────────┐
1900
1673
  // │ Workers don't have fs, path, os, etc. Redirect to polyfills │
1901
1674
  // │ that throw helpful errors if user code tries to use them. │
1902
1675
  // └─────────────────────────────────────────────────────────────────┘
1903
- fs: join6(edgePlaySrc, "polyfills.js"),
1904
- "fs/promises": join6(edgePlaySrc, "polyfills.js"),
1905
- path: join6(edgePlaySrc, "polyfills.js"),
1906
- os: join6(edgePlaySrc, "polyfills.js"),
1907
- process: join6(edgePlaySrc, "polyfills.js")
1676
+ fs: join7(edgePlaySrc, "polyfills.js"),
1677
+ "fs/promises": join7(edgePlaySrc, "polyfills.js"),
1678
+ path: join7(edgePlaySrc, "polyfills.js"),
1679
+ os: join7(edgePlaySrc, "polyfills.js"),
1680
+ process: join7(edgePlaySrc, "polyfills.js")
1908
1681
  },
1909
1682
  // ──── Build Plugins ────
1910
1683
  plugins: [textLoaderPlugin()],
@@ -1921,7 +1694,8 @@ async function bundleBackend(config, options = {}) {
1921
1694
  ...config,
1922
1695
  __routeMetadata: customRouteData
1923
1696
  };
1924
- const entryCode = generateEntryCode(customRouteData, customRoutesDir);
1697
+ const hasAuth = !!config.integrations?.auth;
1698
+ const entryCode = generateEntryCode(customRouteData, customRoutesDir, hasAuth);
1925
1699
  const paths = resolveEmbeddedSourcePaths();
1926
1700
  const buildConfig = createEsbuildConfig(
1927
1701
  entryCode,
@@ -1941,7 +1715,7 @@ async function bundleBackend(config, options = {}) {
1941
1715
  customRoutes: customRouteData
1942
1716
  };
1943
1717
  }
1944
- function generateEntryCode(customRoutes, customRoutesDir) {
1718
+ function generateEntryCode(customRoutes, customRoutesDir, hasAuth) {
1945
1719
  if (customRoutes.length === 0) {
1946
1720
  return entryTemplate;
1947
1721
  }
@@ -1950,9 +1724,10 @@ function generateEntryCode(customRoutes, customRoutesDir) {
1950
1724
  const importPath = route.file.startsWith(customRoutesPrefix) ? route.file.slice(customRoutesPrefix.length) : route.file;
1951
1725
  return `import * as customRoute${i} from '@game-api/${importPath}'`;
1952
1726
  }).join("\n");
1953
- const lastImportIdx = entryTemplate.lastIndexOf("import type");
1954
- const afterLastImport = entryTemplate.indexOf("\n", lastImportIdx) + 1;
1955
- const withImports = entryTemplate.slice(0, afterLastImport) + "\n" + importStatements + "\n" + entryTemplate.slice(afterLastImport);
1727
+ const withImports = entryTemplate.replace(
1728
+ "// \u26A0\uFE0F BUILD_MARKER: CUSTOM_ROUTE_IMPORTS \u26A0\uFE0F",
1729
+ importStatements
1730
+ );
1956
1731
  const registrationStatements = customRoutes.flatMap((route, i) => {
1957
1732
  const methods = ["GET", "POST", "PUT", "PATCH", "DELETE"];
1958
1733
  return methods.map((method) => {
@@ -1961,29 +1736,33 @@ function generateEntryCode(customRoutes, customRoutesDir) {
1961
1736
  });
1962
1737
  });
1963
1738
  const registrationCode = ["// Custom routes", ...registrationStatements, ""].join("\n");
1964
- const exportIdx = withImports.indexOf("export default app");
1965
- const result = withImports.slice(0, exportIdx) + registrationCode + withImports.slice(exportIdx);
1739
+ const withRoutes = withImports.replace("// \u26A0\uFE0F BUILD_MARKER: CUSTOM_ROUTES \u26A0\uFE0F", registrationCode);
1740
+ const sessionMiddlewareStatements = hasAuth ? [
1741
+ "import { createSessionMiddleware } from '@playcademy/edge-play'",
1742
+ "import { getAuth } from '@game-server/lib/auth'",
1743
+ "app.use('*', createSessionMiddleware(getAuth))"
1744
+ ] : [];
1745
+ const sessionMiddlewareCode = sessionMiddlewareStatements.join("\n");
1746
+ const result = withRoutes.replace(
1747
+ "// \u26A0\uFE0F BUILD_MARKER: SESSION_MIDDLEWARE \u26A0\uFE0F",
1748
+ sessionMiddlewareCode
1749
+ );
1966
1750
  return result;
1967
1751
  }
1968
1752
 
1969
1753
  // src/lib/init/prompts.ts
1970
1754
  import { checkbox, confirm, input, select } from "@inquirer/prompts";
1971
1755
  import { bold as bold3, cyan as cyan2 } from "colorette";
1972
- init_constants2();
1973
1756
 
1974
- // src/lib/init/scaffold.ts
1975
- init_constants2();
1976
- init_core();
1977
- init_loader2();
1757
+ // src/lib/init/auth.ts
1758
+ var authTemplate = loadTemplateString("auth/auth.ts");
1759
+ var authCatchAllTemplate = loadTemplateString("auth/auth-catch-all.ts");
1760
+ var authSchemaTemplate = loadTemplateString("auth/auth-schema.ts");
1761
+ var protectedRouteTemplate = loadTemplateString("api/sample-protected.ts");
1978
1762
 
1979
1763
  // src/lib/init/database.ts
1980
- init_log();
1981
- init_slug();
1982
- init_core();
1983
- init_logger();
1984
- init_loader2();
1985
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
1986
- import { join as join7 } from "path";
1764
+ import { existsSync as existsSync7, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1765
+ import { join as join8 } from "path";
1987
1766
  var drizzleConfigTemplate = loadTemplateString("database/drizzle-config.ts");
1988
1767
  var dbSchemaUsersTemplate = loadTemplateString("database/db-schema-users.ts");
1989
1768
  var dbSchemaScoresTemplate = loadTemplateString("database/db-schema-scores.ts");
@@ -1994,9 +1773,9 @@ var dbSeedTemplate = loadTemplateString("database/db-seed.ts");
1994
1773
  var packageTemplate = loadTemplateString("database/package.json");
1995
1774
  function hasDatabaseSetup() {
1996
1775
  const workspace = getWorkspace();
1997
- const drizzleConfigPath = join7(workspace, "drizzle.config.ts");
1998
- const drizzleConfigJsPath = join7(workspace, "drizzle.config.js");
1999
- return existsSync6(drizzleConfigPath) || existsSync6(drizzleConfigJsPath);
1776
+ const drizzleConfigPath = join8(workspace, "drizzle.config.ts");
1777
+ const drizzleConfigJsPath = join8(workspace, "drizzle.config.js");
1778
+ return existsSync7(drizzleConfigPath) || existsSync7(drizzleConfigJsPath);
2000
1779
  }
2001
1780
 
2002
1781
  // src/lib/init/scaffold.ts
@@ -2006,11 +1785,6 @@ var sampleKvRouteTemplate = loadTemplateString("api/sample-kv.ts");
2006
1785
  var sampleBucketRouteTemplate = loadTemplateString("api/sample-bucket.ts");
2007
1786
  var playcademyGitignoreTemplate = loadTemplateString("playcademy-gitignore");
2008
1787
 
2009
- // src/lib/init/display.ts
2010
- init_package_manager();
2011
- init_context();
2012
- init_logger();
2013
-
2014
1788
  // src/lib/init/kv.ts
2015
1789
  function hasKVSetup(config) {
2016
1790
  return !!config.integrations?.kv;
@@ -2023,106 +1797,27 @@ function hasBucketSetup(config) {
2023
1797
 
2024
1798
  // src/lib/init/types.ts
2025
1799
  init_file_loader();
2026
- init_package_manager();
2027
- init_string();
2028
- init_constants2();
2029
- init_loader();
2030
- init_core();
2031
1800
  import { execSync as execSync2 } from "child_process";
2032
- import { writeFileSync as writeFileSync4 } from "fs";
1801
+ import { existsSync as existsSync10, writeFileSync as writeFileSync5 } from "fs";
2033
1802
  import { dirname as dirname4, join as join11 } from "path";
2034
1803
  import { fileURLToPath as fileURLToPath2 } from "url";
2035
1804
 
2036
1805
  // src/lib/deploy/backend.ts
2037
- init_src2();
2038
- init_constants2();
2039
- init_core();
2040
- import { existsSync as existsSync7 } from "node:fs";
2041
- import { join as join8 } from "node:path";
2042
-
2043
- // src/lib/integrations/timeback.ts
2044
- init_src2();
2045
- init_core();
2046
-
2047
- // src/lib/integrations/utils.ts
2048
- init_string();
2049
-
2050
- // src/lib/deploy/schema.ts
2051
- init_constants2();
2052
- init_config3();
2053
- init_core();
2054
-
2055
- // src/lib/deploy/secrets.ts
2056
- init_core();
2057
- init_logger();
2058
-
2059
- // src/lib/deploy/utils.ts
2060
- init_src();
2061
-
2062
- // src/lib/deploy/backend.ts
1806
+ import { existsSync as existsSync8 } from "node:fs";
1807
+ import { join as join9 } from "node:path";
2063
1808
  function getCustomRoutesDirectory(projectPath, config) {
2064
1809
  const customRoutes = config?.integrations?.customRoutes;
2065
1810
  const customRoutesDir = typeof customRoutes === "object" && customRoutes.directory || DEFAULT_API_ROUTES_DIRECTORY;
2066
- return join8(projectPath, customRoutesDir);
1811
+ return join9(projectPath, customRoutesDir);
2067
1812
  }
2068
1813
  function hasLocalCustomRoutes(projectPath, config) {
2069
1814
  const customRoutesDir = getCustomRoutesDirectory(projectPath, config);
2070
- return existsSync7(customRoutesDir);
2071
- }
2072
-
2073
- // src/lib/secrets/env.ts
2074
- init_file_loader();
2075
- init_constants2();
2076
- import { existsSync as existsSync8 } from "fs";
2077
- import { join as join9 } from "path";
2078
- function parseEnvFile(contents) {
2079
- const secrets = {};
2080
- for (const line of contents.split("\n")) {
2081
- const trimmed = line.trim();
2082
- if (!trimmed || trimmed.startsWith("#")) {
2083
- continue;
2084
- }
2085
- const match = trimmed.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
2086
- if (match) {
2087
- const [, key, value] = match;
2088
- if (!key || !value) {
2089
- continue;
2090
- }
2091
- const unquoted = value.replace(/^["']|["']$/g, "");
2092
- secrets[key] = unquoted;
2093
- }
2094
- }
2095
- return secrets;
2096
- }
2097
- async function readEnvFile(workspace) {
2098
- let secrets = {};
2099
- for (const filename of ENV_FILES) {
2100
- try {
2101
- const contents = await loadFile(filename, { cwd: workspace, searchUp: false });
2102
- if (contents) {
2103
- const fileSecrets = parseEnvFile(contents);
2104
- secrets = { ...secrets, ...fileSecrets };
2105
- }
2106
- } catch {
2107
- continue;
2108
- }
2109
- }
2110
- return secrets;
2111
- }
2112
- function getLoadedEnvFiles(workspace) {
2113
- return ENV_FILES.filter((filename) => existsSync8(join9(workspace, filename)));
1815
+ return existsSync8(customRoutesDir);
2114
1816
  }
2115
- function hasEnvFile(workspace) {
2116
- return ENV_FILES.some((filename) => existsSync8(join9(workspace, filename)));
2117
- }
2118
-
2119
- // src/lib/init/types.ts
2120
- init_loader2();
2121
1817
 
2122
1818
  // src/lib/init/tsconfig.ts
2123
1819
  init_file_loader();
2124
- init_constants2();
2125
- import { existsSync as existsSync9, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
1820
+ import { existsSync as existsSync9, readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
2126
1821
  import { join as join10 } from "path";
2127
1822
  function hasPlaycademyEnv(config) {
2128
1823
  return config.include?.includes("playcademy-env.d.ts") ?? false;
@@ -2200,13 +1895,13 @@ async function ensureTsconfigIncludes(workspace) {
2200
1895
  const rawContent = readFileSync4(configPath, "utf-8");
2201
1896
  const updatedContent = addToIncludeArrayPreservingComments(rawContent);
2202
1897
  if (updatedContent && updatedContent !== rawContent) {
2203
- writeFileSync3(configPath, updatedContent);
1898
+ writeFileSync4(configPath, updatedContent);
2204
1899
  return filename;
2205
1900
  }
2206
1901
  } catch {
2207
1902
  }
2208
1903
  addPlaycademyEnv(config);
2209
- writeFileSync3(configPath, JSON.stringify(config, null, 4) + "\n");
1904
+ writeFileSync4(configPath, JSON.stringify(config, null, 4) + "\n");
2210
1905
  return filename;
2211
1906
  } catch {
2212
1907
  continue;
@@ -2241,7 +1936,7 @@ async function setupPlaycademyDependencies(workspace) {
2241
1936
  dependencies: { hono: honoVersion },
2242
1937
  devDependencies: { "@cloudflare/workers-types": workersTypesVersion }
2243
1938
  };
2244
- writeFileSync4(playcademyPkgPath, JSON.stringify(playcademyPkg, null, 4) + "\n");
1939
+ writeFileSync5(playcademyPkgPath, JSON.stringify(playcademyPkg, null, 4) + "\n");
2245
1940
  const pm = detectPackageManager(workspace);
2246
1941
  const installCmd = getInstallCommand(pm);
2247
1942
  execSync2(installCmd, {
@@ -2256,6 +1951,22 @@ function generateBindingsTypeString(features) {
2256
1951
  if (features.hasBucket) bindings.push(" BUCKET: R2Bucket");
2257
1952
  return bindings.length > 0 ? "\n" + bindings.join("\n") : "";
2258
1953
  }
1954
+ function generateAuthVariablesString(hasAuth, hasAuthFile) {
1955
+ const authImport = hasAuthFile ? "\nimport type { UserInfo } from './node_modules/@playcademy/sdk/dist/server.d.ts'\nimport type { User } from './server/lib/auth'" : "\nimport type { UserInfo } from './node_modules/@playcademy/sdk/dist/server.d.ts'";
1956
+ const variableFields = [" playcademyUser?: UserInfo"];
1957
+ if (hasAuth && hasAuthFile) {
1958
+ variableFields.push(" user?: User");
1959
+ }
1960
+ return {
1961
+ authImport,
1962
+ variables: `
1963
+
1964
+ interface PlaycademyVariables {
1965
+ ${variableFields.join("\n")}
1966
+ }`,
1967
+ contextVars: "; Variables: PlaycademyVariables"
1968
+ };
1969
+ }
2259
1970
  async function generateSecretsTypeString(workspace, verbose = false) {
2260
1971
  try {
2261
1972
  const envSecrets = await readEnvFile(workspace);
@@ -2299,10 +2010,16 @@ async function ensurePlaycademyTypes(options = {}) {
2299
2010
  }
2300
2011
  const bindingsStr = generateBindingsTypeString(features);
2301
2012
  const secretsStr = await generateSecretsTypeString(workspace, verbose);
2013
+ const hasAuth = !!config.integrations?.auth;
2014
+ const hasAuthFile = existsSync10(join11(workspace, "server/lib/auth.ts"));
2015
+ const authVariablesString = generateAuthVariablesString(hasAuth, hasAuthFile);
2302
2016
  let envContent = playcademyEnvTemplate.replace("{{BINDINGS}}", bindingsStr);
2303
2017
  envContent = envContent.replace("{{SECRETS}}", secretsStr);
2018
+ envContent = envContent.replace("{{AUTH_IMPORT}}", authVariablesString.authImport);
2019
+ envContent = envContent.replace("{{VARIABLES}}", authVariablesString.variables);
2020
+ envContent = envContent.replace("{{CONTEXT_VARS}}", authVariablesString.contextVars);
2304
2021
  const envPath = join11(workspace, "playcademy-env.d.ts");
2305
- writeFileSync4(envPath, envContent);
2022
+ writeFileSync5(envPath, envContent);
2306
2023
  if (verbose) {
2307
2024
  logger.success(`Generated <playcademy-env.d.ts>`);
2308
2025
  }
@@ -2310,7 +2027,6 @@ async function ensurePlaycademyTypes(options = {}) {
2310
2027
  if (verbose) {
2311
2028
  logger.success(`Updated <${tsConfigFilename}>`);
2312
2029
  }
2313
- logger.newLine();
2314
2030
  } catch (error) {
2315
2031
  logger.warn(
2316
2032
  `Failed to generate TypeScript types: ${error instanceof Error ? error.message : String(error)}`
@@ -2319,19 +2035,33 @@ async function ensurePlaycademyTypes(options = {}) {
2319
2035
  }
2320
2036
 
2321
2037
  // src/lib/init/gitignore.ts
2322
- init_core();
2323
- init_loader2();
2324
2038
  var rootGitignoreTemplate = loadTemplateString("gitignore");
2325
2039
 
2326
2040
  // src/lib/dev/server.ts
2327
2041
  var FilteredLog = class extends Log {
2042
+ customLogger;
2043
+ constructor(logLevel, customLogger) {
2044
+ super(logLevel);
2045
+ this.customLogger = customLogger;
2046
+ }
2328
2047
  logWithLevel(level, message) {
2329
2048
  if (message.includes("Ready on")) return;
2330
- console.log(message);
2049
+ if (message.includes("Updated `Request.cf` object cache!")) return;
2050
+ if (this.customLogger) {
2051
+ this.customLogger.info(message);
2052
+ } else {
2053
+ console.log(message);
2054
+ }
2331
2055
  }
2332
2056
  };
2333
2057
  async function startDevServer(options) {
2334
- const { port: preferredPort, config: providedConfig, platformUrl, logger: logger2 = true } = options;
2058
+ const {
2059
+ port: preferredPort,
2060
+ config: providedConfig,
2061
+ platformUrl,
2062
+ logger: logger2 = true,
2063
+ customLogger
2064
+ } = options;
2335
2065
  const port = await findAvailablePort(preferredPort);
2336
2066
  const config = providedConfig ?? await loadConfig();
2337
2067
  await ensurePlaycademyTypes();
@@ -2349,7 +2079,7 @@ async function startDevServer(options) {
2349
2079
  const bucketDir = hasBucket ? await ensureBucketDirectory() : void 0;
2350
2080
  const workspace = getWorkspace();
2351
2081
  const envSecrets = await readEnvFile(workspace);
2352
- const log2 = logger2 ? new FilteredLog(LogLevel.INFO) : new Log(LogLevel.NONE);
2082
+ const log2 = logger2 ? new FilteredLog(LogLevel.INFO, customLogger) : new Log(LogLevel.NONE);
2353
2083
  const sandboxInfo = readServerInfo("sandbox", workspace);
2354
2084
  const baseUrl = platformUrl ?? sandboxInfo?.url ?? process.env.PLAYCADEMY_BASE_URL ?? `http://localhost:${DEFAULT_PORTS.SANDBOX}`;
2355
2085
  const bindings = {
@@ -2377,7 +2107,18 @@ async function startDevServer(options) {
2377
2107
  kvPersist: kvDir,
2378
2108
  r2Buckets: hasBucket ? [CLOUDFLARE_BINDINGS.BUCKET] : [],
2379
2109
  r2Persist: bucketDir,
2380
- compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
2110
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE,
2111
+ /**
2112
+ * Enable Node.js compatibility for libraries that use Node.js APIs (like AsyncLocalStorage).
2113
+ * This was added when integrating Better Auth, which requires AsyncLocalStorage for session
2114
+ * management. No other integration has required this flag yet, but it provides additional
2115
+ * flexibility for users who may want to use other libraries that depend on Node.js APIs in
2116
+ * their custom routes. Future consideration: We may want to make this conditional based on
2117
+ * detected dependencies (e.g., only enable if better-auth is in package.json), but enabling
2118
+ * it by default seems reasonable given the tradeoff of better compatibility vs. minimal
2119
+ * performance impact.
2120
+ */
2121
+ compatibilityFlags: ["nodejs_compat"]
2381
2122
  });
2382
2123
  if (hasDatabase) {
2383
2124
  await initializeDatabase(mf);
@@ -2427,8 +2168,6 @@ async function writeBackendServerInfo(port) {
2427
2168
  }
2428
2169
 
2429
2170
  // src/lib/dev/reload.ts
2430
- init_constants2();
2431
- init_core();
2432
2171
  import { join as join13, relative as relative2 } from "path";
2433
2172
  import chokidar from "chokidar";
2434
2173
  import { bold as bold4, cyan as cyan3, dim as dim3, green as green2 } from "colorette";
@@ -2486,9 +2225,6 @@ function startHotReload(onReload, options = {}) {
2486
2225
  watcher.on("unlink", createReloadHandler("removed"));
2487
2226
  return watcher;
2488
2227
  }
2489
-
2490
- // src/utils.ts
2491
- init_import();
2492
2228
  export {
2493
2229
  findConfigPath as findPlaycademyConfigPath,
2494
2230
  importTypescriptDefault,