@playcademy/sdk 0.6.1-beta.3 → 0.6.1-beta.5

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/types.d.ts CHANGED
@@ -332,6 +332,14 @@ interface GameTimebackIntegration {
332
332
  createdAt: Date;
333
333
  updatedAt: Date;
334
334
  }
335
+ type TimebackPromotionStatus = 'promoted' | 'no-next-course' | 'already-promoted' | 'not-enrolled' | 'not-mastered';
336
+ interface TimebackPromotionResult {
337
+ status: TimebackPromotionStatus;
338
+ currentCourseId: string;
339
+ nextCourseId?: string;
340
+ masteredUnits?: number;
341
+ masterableUnits?: number;
342
+ }
335
343
  interface EndActivityResponse {
336
344
  status: 'ok';
337
345
  courseId: string;
@@ -341,98 +349,200 @@ interface EndActivityResponse {
341
349
  scoreStatus?: string;
342
350
  inProgress?: string;
343
351
  }
352
+ interface AdvanceCourseResponse {
353
+ status: 'ok';
354
+ promotion: TimebackPromotionResult;
355
+ }
344
356
 
345
357
  /**
346
- * Achievement Types
358
+ * @fileoverview Server SDK Type Definitions
347
359
  *
348
- * @module types/achievement
360
+ * TypeScript type definitions for the server-side Playcademy SDK.
361
+ * Includes configuration types, client state, and re-exported TimeBack types.
349
362
  */
350
- type AchievementScopeType = 'daily' | 'weekly' | 'monthly' | 'yearly' | 'game' | 'global' | 'map' | 'level' | 'event';
351
- declare enum AchievementCompletionType {
352
- TIME_PLAYED_SESSION = "time_played_session",
353
- INTERACTION = "interaction",
354
- LEADERBOARD_RANK = "leaderboard_rank",
355
- FIRST_SCORE = "first_score",
356
- PERSONAL_BEST = "personal_best"
363
+
364
+ /**
365
+ * Base configuration for TimeBack integration (shared across all courses).
366
+ * References upstream TimeBack types from @playcademy/timeback.
367
+ *
368
+ * All fields are optional and support template variables: {grade}, {subject}, {gameSlug}
369
+ */
370
+ interface TimebackBaseConfig {
371
+ /** Organization configuration (shared across all courses) */
372
+ organization?: Partial<OrganizationConfig>;
373
+ /** Course defaults (can be overridden per-course) */
374
+ course?: Partial<CourseConfig>;
375
+ /** Component defaults */
376
+ component?: Partial<ComponentConfig>;
377
+ /** Resource defaults */
378
+ resource?: Partial<ResourceConfig>;
379
+ /** ComponentResource defaults */
380
+ componentResource?: Partial<ComponentResourceConfig>;
357
381
  }
358
- interface AchievementCurrent {
359
- id: string;
360
- title: string;
361
- description?: string | null;
362
- scope: AchievementScopeType;
363
- rewardCredits: number;
364
- limit: number;
365
- completionType: string;
366
- completionConfig: unknown;
367
- target: unknown;
368
- active: boolean;
369
- createdAt?: Date | null;
370
- updatedAt?: Date | null;
371
- status: 'available' | 'completed';
372
- scopeKey: string;
373
- windowStart: string;
374
- windowEnd: string;
382
+ /**
383
+ * Extended course configuration that merges TimebackCourseConfig with per-course overrides.
384
+ * Used in playcademy.config.* to allow per-course customization.
385
+ */
386
+ interface TimebackCourseConfigWithOverrides extends TimebackCourseConfig {
387
+ title?: string;
388
+ courseCode?: string;
389
+ level?: string;
390
+ metadata?: CourseConfig['metadata'];
391
+ totalXp?: number | null;
392
+ masterableUnits?: number | null;
375
393
  }
376
- interface AchievementWithStatus {
377
- id: string;
378
- title: string;
379
- description: string | null;
380
- scope: AchievementScopeType;
381
- rewardCredits: number;
382
- limit: number;
383
- completionType: string;
384
- completionConfig: unknown;
385
- target: unknown;
386
- active: boolean;
387
- createdAt: Date | null;
388
- updatedAt: Date | null;
389
- status: 'available' | 'completed';
390
- scopeKey: string;
391
- windowStart?: string;
392
- windowEnd?: string;
394
+ /**
395
+ * TimeBack integration configuration for Playcademy config file.
396
+ *
397
+ * Supports two levels of customization:
398
+ * 1. `base`: Shared defaults for all courses (organization, course, component, resource, componentResource)
399
+ * 2. Per-course overrides in the `courses` array (title, courseCode, level, gradingScheme, metadata)
400
+ *
401
+ * Template variables ({grade}, {subject}, {gameSlug}) can be used in string fields.
402
+ */
403
+ interface TimebackIntegrationConfig {
404
+ /** Multi-grade course configuration (array of grade/subject/totalXp with optional per-course overrides) */
405
+ courses: TimebackCourseConfigWithOverrides[];
406
+ /** Optional base configuration (shared across all courses, can be overridden per-course) */
407
+ base?: TimebackBaseConfig;
393
408
  }
394
- interface AchievementHistoryEntry {
395
- achievementId: string;
396
- title: string;
397
- rewardCredits: number;
398
- createdAt: Date;
399
- scopeKey: string;
409
+ /**
410
+ * Custom API routes integration
411
+ */
412
+ interface CustomRoutesIntegration {
413
+ /** Directory for custom API routes (defaults to 'server/api') */
414
+ directory?: string;
400
415
  }
401
- interface AchievementProgressResponse {
402
- achievementId: string;
403
- status: 'completed' | 'already_completed';
404
- rewardCredits: number;
405
- scopeKey: string;
406
- createdAt: Date;
416
+ /**
417
+ * Database integration
418
+ */
419
+ interface DatabaseIntegration {
420
+ /** Database directory (defaults to 'db') */
421
+ directory?: string;
422
+ /** Schema strategy: 'push' uses drizzle-kit push-style diffing, 'migrate' uses migration files.
423
+ * When omitted, auto-detects based on presence of a migrations directory with _journal.json. */
424
+ strategy?: 'push' | 'migrate';
425
+ }
426
+ interface QueueConfig {
427
+ maxBatchSize?: number;
428
+ maxRetries?: number;
429
+ maxBatchTimeout?: number;
430
+ maxConcurrency?: number;
431
+ retryDelay?: number;
432
+ deadLetterQueue?: string;
433
+ }
434
+ /**
435
+ * Integrations configuration
436
+ * All backend features (database, custom routes, external services) are configured here
437
+ */
438
+ interface IntegrationsConfig {
439
+ /** TimeBack integration (optional) */
440
+ timeback?: TimebackIntegrationConfig | null;
441
+ /** Custom API routes (optional) */
442
+ customRoutes?: CustomRoutesIntegration | boolean;
443
+ /** Database (optional) */
444
+ database?: DatabaseIntegration | boolean;
445
+ /** Key-Value storage (optional) */
446
+ kv?: boolean;
447
+ /** Bucket storage (optional) */
448
+ bucket?: boolean;
449
+ /** Authentication (optional) */
450
+ auth?: boolean;
451
+ /** Queues (optional) */
452
+ queues?: Record<string, QueueConfig | boolean>;
453
+ }
454
+ /**
455
+ * Unified Playcademy configuration
456
+ * Used for playcademy.config.{js,json}
457
+ */
458
+ interface PlaycademyConfig {
459
+ /** Game name */
460
+ name: string;
461
+ /** Game description */
462
+ description?: string;
463
+ /** Game emoji icon */
464
+ emoji?: string;
465
+ /** Build command to run before deployment */
466
+ buildCommand?: string[];
467
+ /** Path to build output */
468
+ buildPath?: string;
469
+ /** Game type */
470
+ gameType?: 'hosted' | 'external';
471
+ /** External URL (for external games) */
472
+ externalUrl?: string;
473
+ /** Game platform */
474
+ platform?: 'web' | 'unity' | 'godot';
475
+ /** Integrations (database, custom routes, external services) */
476
+ integrations?: IntegrationsConfig;
407
477
  }
408
478
 
409
479
  /**
410
- * Notification Types
480
+ * Configuration options for initializing a PlaycademyClient instance.
411
481
  *
412
- * @module types/notification
482
+ * @example
483
+ * ```typescript
484
+ * const config: PlaycademyServerClientConfig = {
485
+ * apiKey: process.env.PLAYCADEMY_API_KEY!,
486
+ * gameId: 'my-math-game',
487
+ * configPath: './playcademy.config.js'
488
+ * }
489
+ * ```
413
490
  */
414
-
415
- declare enum NotificationType {
416
- ACHIEVEMENT = "achievement",
417
- SYSTEM = "system",
418
- PROMO = "promo"
419
- }
420
- declare enum NotificationStatus {
421
- PENDING = "pending",
422
- DELIVERED = "delivered",
423
- SEEN = "seen",
424
- CLICKED = "clicked",
425
- DISMISSED = "dismissed",
426
- EXPIRED = "expired"
491
+ interface PlaycademyServerClientConfig {
492
+ /**
493
+ * Playcademy API key for server-to-server authentication.
494
+ * Obtain from the Playcademy developer dashboard.
495
+ */
496
+ apiKey: string;
497
+ /**
498
+ * Optional path to playcademy.config.js file.
499
+ * If not provided, searches current directory and up to 3 parent directories.
500
+ * Ignored if `config` is provided directly.
501
+ *
502
+ * @example './config/playcademy.config.js'
503
+ */
504
+ configPath?: string;
505
+ /**
506
+ * Optional config object (for edge environments without filesystem).
507
+ * If provided, skips filesystem-based config loading.
508
+ *
509
+ * @example { name: 'My Game', integrations: { timeback: {...} } }
510
+ */
511
+ config?: PlaycademyConfig;
512
+ /**
513
+ * Optional base URL for Playcademy API.
514
+ * Defaults to environment variables or 'https://hub.playcademy.net'.
515
+ *
516
+ * @example 'http://localhost:3000' for local development
517
+ */
518
+ baseUrl?: string;
519
+ /**
520
+ * Optional game ID.
521
+ * If not provided, will attempt to fetch from API using the API token.
522
+ *
523
+ * @example 'my-math-game'
524
+ */
525
+ gameId?: string;
427
526
  }
428
- interface NotificationStats {
429
- total: number;
430
- delivered: number;
431
- seen: number;
432
- clicked: number;
433
- dismissed: number;
434
- expired: number;
435
- clickThroughRate: number;
527
+ /**
528
+ * Internal state maintained by the PlaycademyClient instance.
529
+ *
530
+ * @internal
531
+ */
532
+ interface PlaycademyServerClientState {
533
+ /** API key for authentication */
534
+ apiKey: string;
535
+ /** Base URL for API requests */
536
+ baseUrl: string;
537
+ /** Game identifier */
538
+ gameId: string;
539
+ /** Loaded game configuration from playcademy.config.js */
540
+ config: PlaycademyConfig;
541
+ /**
542
+ * TimeBack course ID fetched from the Playcademy API.
543
+ * Used for all TimeBack event recording.
544
+ */
545
+ courseId?: string;
436
546
  }
437
547
 
438
548
  /**
@@ -597,30 +707,94 @@ interface UserRankResponse {
597
707
  score: number;
598
708
  userId: string;
599
709
  }
600
- interface UserScore {
601
- id: string;
602
- score: number;
603
- achievedAt: Date;
604
- metadata?: Record<string, unknown>;
605
- gameId: string;
606
- gameTitle: string;
607
- gameSlug: string;
710
+ interface UserScore {
711
+ id: string;
712
+ score: number;
713
+ achievedAt: Date;
714
+ metadata?: Record<string, unknown>;
715
+ gameId: string;
716
+ gameTitle: string;
717
+ gameSlug: string;
718
+ }
719
+ /**
720
+ * Leaderboard entry with required game context.
721
+ * Used when fetching leaderboards for a specific game.
722
+ */
723
+ interface GameLeaderboardEntry {
724
+ rank: number;
725
+ userId: string;
726
+ username: string;
727
+ userImage?: string | null;
728
+ score: number;
729
+ achievedAt: Date;
730
+ metadata?: Record<string, unknown>;
731
+ gameId: string;
732
+ gameTitle: string;
733
+ gameSlug: string;
734
+ }
735
+
736
+ /**
737
+ * Achievement Types
738
+ *
739
+ * @module types/achievement
740
+ */
741
+ type AchievementScopeType = 'daily' | 'weekly' | 'monthly' | 'yearly' | 'game' | 'global' | 'map' | 'level' | 'event';
742
+ declare enum AchievementCompletionType {
743
+ TIME_PLAYED_SESSION = "time_played_session",
744
+ INTERACTION = "interaction",
745
+ LEADERBOARD_RANK = "leaderboard_rank",
746
+ FIRST_SCORE = "first_score",
747
+ PERSONAL_BEST = "personal_best"
748
+ }
749
+ interface AchievementCurrent {
750
+ id: string;
751
+ title: string;
752
+ description?: string | null;
753
+ scope: AchievementScopeType;
754
+ rewardCredits: number;
755
+ limit: number;
756
+ completionType: string;
757
+ completionConfig: unknown;
758
+ target: unknown;
759
+ active: boolean;
760
+ createdAt?: Date | null;
761
+ updatedAt?: Date | null;
762
+ status: 'available' | 'completed';
763
+ scopeKey: string;
764
+ windowStart: string;
765
+ windowEnd: string;
766
+ }
767
+ interface AchievementWithStatus {
768
+ id: string;
769
+ title: string;
770
+ description: string | null;
771
+ scope: AchievementScopeType;
772
+ rewardCredits: number;
773
+ limit: number;
774
+ completionType: string;
775
+ completionConfig: unknown;
776
+ target: unknown;
777
+ active: boolean;
778
+ createdAt: Date | null;
779
+ updatedAt: Date | null;
780
+ status: 'available' | 'completed';
781
+ scopeKey: string;
782
+ windowStart?: string;
783
+ windowEnd?: string;
784
+ }
785
+ interface AchievementHistoryEntry {
786
+ achievementId: string;
787
+ title: string;
788
+ rewardCredits: number;
789
+ createdAt: Date;
790
+ scopeKey: string;
608
791
  }
609
- /**
610
- * Leaderboard entry with required game context.
611
- * Used when fetching leaderboards for a specific game.
612
- */
613
- interface GameLeaderboardEntry {
614
- rank: number;
615
- userId: string;
616
- username: string;
617
- userImage?: string | null;
618
- score: number;
619
- achievedAt: Date;
620
- metadata?: Record<string, unknown>;
621
- gameId: string;
622
- gameTitle: string;
623
- gameSlug: string;
792
+ interface AchievementProgressResponse {
793
+ achievementId: string;
794
+ status: 'completed' | 'already_completed';
795
+ rewardCredits: number;
796
+ scopeKey: string;
797
+ createdAt: Date;
624
798
  }
625
799
 
626
800
  /**
@@ -740,6 +914,35 @@ interface LevelProgressResponse {
740
914
  totalXP: number;
741
915
  }
742
916
 
917
+ /**
918
+ * Notification Types
919
+ *
920
+ * @module types/notification
921
+ */
922
+
923
+ declare enum NotificationType {
924
+ ACHIEVEMENT = "achievement",
925
+ SYSTEM = "system",
926
+ PROMO = "promo"
927
+ }
928
+ declare enum NotificationStatus {
929
+ PENDING = "pending",
930
+ DELIVERED = "delivered",
931
+ SEEN = "seen",
932
+ CLICKED = "clicked",
933
+ DISMISSED = "dismissed",
934
+ EXPIRED = "expired"
935
+ }
936
+ interface NotificationStats {
937
+ total: number;
938
+ delivered: number;
939
+ seen: number;
940
+ clicked: number;
941
+ dismissed: number;
942
+ expired: number;
943
+ clickThroughRate: number;
944
+ }
945
+
743
946
  /**
744
947
  * Shop Types
745
948
  *
@@ -1064,7 +1267,7 @@ declare const users: drizzle_orm_pg_core.PgTableWithColumns<{
1064
1267
  generated: undefined;
1065
1268
  }, {}, {}>;
1066
1269
  };
1067
- dialect: "pg";
1270
+ dialect: 'pg';
1068
1271
  }>;
1069
1272
 
1070
1273
  interface GameMetadata {
@@ -1340,7 +1543,7 @@ declare const games: drizzle_orm_pg_core.PgTableWithColumns<{
1340
1543
  generated: undefined;
1341
1544
  }, {}, {}>;
1342
1545
  };
1343
- dialect: "pg";
1546
+ dialect: 'pg';
1344
1547
  }>;
1345
1548
  declare const gameSessions: drizzle_orm_pg_core.PgTableWithColumns<{
1346
1549
  name: "game_sessions";
@@ -1432,7 +1635,7 @@ declare const gameSessions: drizzle_orm_pg_core.PgTableWithColumns<{
1432
1635
  generated: undefined;
1433
1636
  }, {}, {}>;
1434
1637
  };
1435
- dialect: "pg";
1638
+ dialect: 'pg';
1436
1639
  }>;
1437
1640
  /**
1438
1641
  * Custom hostnames table
@@ -1633,7 +1836,7 @@ declare const gameCustomHostnames: drizzle_orm_pg_core.PgTableWithColumns<{
1633
1836
  generated: undefined;
1634
1837
  }, {}, {}>;
1635
1838
  };
1636
- dialect: "pg";
1839
+ dialect: 'pg';
1637
1840
  }>;
1638
1841
  declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
1639
1842
  name: "items";
@@ -1810,7 +2013,7 @@ declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
1810
2013
  generated: undefined;
1811
2014
  }, {}, {}>;
1812
2015
  };
1813
- dialect: "pg";
2016
+ dialect: 'pg';
1814
2017
  }>;
1815
2018
  declare const inventoryItems: drizzle_orm_pg_core.PgTableWithColumns<{
1816
2019
  name: "inventory_items";
@@ -1902,7 +2105,7 @@ declare const inventoryItems: drizzle_orm_pg_core.PgTableWithColumns<{
1902
2105
  generated: undefined;
1903
2106
  }, {}, {}>;
1904
2107
  };
1905
- dialect: "pg";
2108
+ dialect: 'pg';
1906
2109
  }>;
1907
2110
  declare const currencies: drizzle_orm_pg_core.PgTableWithColumns<{
1908
2111
  name: "currencies";
@@ -2011,7 +2214,7 @@ declare const currencies: drizzle_orm_pg_core.PgTableWithColumns<{
2011
2214
  generated: undefined;
2012
2215
  }, {}, {}>;
2013
2216
  };
2014
- dialect: "pg";
2217
+ dialect: 'pg';
2015
2218
  }>;
2016
2219
  declare const shopListings: drizzle_orm_pg_core.PgTableWithColumns<{
2017
2220
  name: "shop_listings";
@@ -2205,7 +2408,7 @@ declare const shopListings: drizzle_orm_pg_core.PgTableWithColumns<{
2205
2408
  generated: undefined;
2206
2409
  }, {}, {}>;
2207
2410
  };
2208
- dialect: "pg";
2411
+ dialect: 'pg';
2209
2412
  }>;
2210
2413
  declare const maps: drizzle_orm_pg_core.PgTableWithColumns<{
2211
2414
  name: "maps";
@@ -2356,7 +2559,7 @@ declare const maps: drizzle_orm_pg_core.PgTableWithColumns<{
2356
2559
  generated: undefined;
2357
2560
  }, {}, {}>;
2358
2561
  };
2359
- dialect: "pg";
2562
+ dialect: 'pg';
2360
2563
  }>;
2361
2564
  declare const mapElements: drizzle_orm_pg_core.PgTableWithColumns<{
2362
2565
  name: "map_elements";
@@ -2469,7 +2672,7 @@ declare const mapElements: drizzle_orm_pg_core.PgTableWithColumns<{
2469
2672
  $type: Record<string, unknown>;
2470
2673
  }>;
2471
2674
  };
2472
- dialect: "pg";
2675
+ dialect: 'pg';
2473
2676
  }>;
2474
2677
  declare const mapObjects: drizzle_orm_pg_core.PgTableWithColumns<{
2475
2678
  name: "map_objects";
@@ -2629,7 +2832,7 @@ declare const mapObjects: drizzle_orm_pg_core.PgTableWithColumns<{
2629
2832
  generated: undefined;
2630
2833
  }, {}, {}>;
2631
2834
  };
2632
- dialect: "pg";
2835
+ dialect: 'pg';
2633
2836
  }>;
2634
2837
 
2635
2838
  declare const userLevels: drizzle_orm_pg_core.PgTableWithColumns<{
@@ -2756,7 +2959,7 @@ declare const userLevels: drizzle_orm_pg_core.PgTableWithColumns<{
2756
2959
  generated: undefined;
2757
2960
  }, {}, {}>;
2758
2961
  };
2759
- dialect: "pg";
2962
+ dialect: 'pg';
2760
2963
  }>;
2761
2964
  declare const levelConfigs: drizzle_orm_pg_core.PgTableWithColumns<{
2762
2965
  name: "level_configs";
@@ -2848,7 +3051,7 @@ declare const levelConfigs: drizzle_orm_pg_core.PgTableWithColumns<{
2848
3051
  generated: undefined;
2849
3052
  }, {}, {}>;
2850
3053
  };
2851
- dialect: "pg";
3054
+ dialect: 'pg';
2852
3055
  }>;
2853
3056
 
2854
3057
  declare const spriteTemplates: drizzle_orm_pg_core.PgTableWithColumns<{
@@ -2945,7 +3148,7 @@ declare const spriteTemplates: drizzle_orm_pg_core.PgTableWithColumns<{
2945
3148
  generated: undefined;
2946
3149
  }, {}, {}>;
2947
3150
  };
2948
- dialect: "pg";
3151
+ dialect: 'pg';
2949
3152
  }>;
2950
3153
  declare const characterComponents: drizzle_orm_pg_core.PgTableWithColumns<{
2951
3154
  name: "character_components";
@@ -3128,7 +3331,7 @@ declare const characterComponents: drizzle_orm_pg_core.PgTableWithColumns<{
3128
3331
  generated: undefined;
3129
3332
  }, {}, {}>;
3130
3333
  };
3131
- dialect: "pg";
3334
+ dialect: 'pg';
3132
3335
  }>;
3133
3336
  declare const playerCharacters: drizzle_orm_pg_core.PgTableWithColumns<{
3134
3337
  name: "player_characters";
@@ -3271,7 +3474,7 @@ declare const playerCharacters: drizzle_orm_pg_core.PgTableWithColumns<{
3271
3474
  generated: undefined;
3272
3475
  }, {}, {}>;
3273
3476
  };
3274
- dialect: "pg";
3477
+ dialect: 'pg';
3275
3478
  }>;
3276
3479
  declare const playerCharacterAccessories: drizzle_orm_pg_core.PgTableWithColumns<{
3277
3480
  name: "player_character_accessories";
@@ -3382,7 +3585,7 @@ declare const playerCharacterAccessories: drizzle_orm_pg_core.PgTableWithColumns
3382
3585
  generated: undefined;
3383
3586
  }, {}, {}>;
3384
3587
  };
3385
- dialect: "pg";
3588
+ dialect: 'pg';
3386
3589
  }>;
3387
3590
  declare const notifications: drizzle_orm_pg_core.PgTableWithColumns<{
3388
3591
  name: "notifications";
@@ -3667,7 +3870,7 @@ declare const notifications: drizzle_orm_pg_core.PgTableWithColumns<{
3667
3870
  generated: undefined;
3668
3871
  }, {}, {}>;
3669
3872
  };
3670
- dialect: "pg";
3873
+ dialect: 'pg';
3671
3874
  }>;
3672
3875
  declare const UpsertGameMetadataSchema: z.ZodEffects<z.ZodObject<{
3673
3876
  displayName: z.ZodString;
@@ -4410,197 +4613,6 @@ type SpriteTemplateRow = typeof spriteTemplates.$inferSelect;
4410
4613
 
4411
4614
  type NotificationRow = InferSelectModel<typeof notifications>;
4412
4615
 
4413
- /**
4414
- * @fileoverview Server SDK Type Definitions
4415
- *
4416
- * TypeScript type definitions for the server-side Playcademy SDK.
4417
- * Includes configuration types, client state, and re-exported TimeBack types.
4418
- */
4419
-
4420
- /**
4421
- * Base configuration for TimeBack integration (shared across all courses).
4422
- * References upstream TimeBack types from @playcademy/timeback.
4423
- *
4424
- * All fields are optional and support template variables: {grade}, {subject}, {gameSlug}
4425
- */
4426
- interface TimebackBaseConfig {
4427
- /** Organization configuration (shared across all courses) */
4428
- organization?: Partial<OrganizationConfig>;
4429
- /** Course defaults (can be overridden per-course) */
4430
- course?: Partial<CourseConfig>;
4431
- /** Component defaults */
4432
- component?: Partial<ComponentConfig>;
4433
- /** Resource defaults */
4434
- resource?: Partial<ResourceConfig>;
4435
- /** ComponentResource defaults */
4436
- componentResource?: Partial<ComponentResourceConfig>;
4437
- }
4438
- /**
4439
- * Extended course configuration that merges TimebackCourseConfig with per-course overrides.
4440
- * Used in playcademy.config.* to allow per-course customization.
4441
- */
4442
- interface TimebackCourseConfigWithOverrides extends TimebackCourseConfig {
4443
- title?: string;
4444
- courseCode?: string;
4445
- level?: string;
4446
- metadata?: CourseConfig['metadata'];
4447
- totalXp?: number | null;
4448
- masterableUnits?: number | null;
4449
- }
4450
- /**
4451
- * TimeBack integration configuration for Playcademy config file.
4452
- *
4453
- * Supports two levels of customization:
4454
- * 1. `base`: Shared defaults for all courses (organization, course, component, resource, componentResource)
4455
- * 2. Per-course overrides in the `courses` array (title, courseCode, level, gradingScheme, metadata)
4456
- *
4457
- * Template variables ({grade}, {subject}, {gameSlug}) can be used in string fields.
4458
- */
4459
- interface TimebackIntegrationConfig {
4460
- /** Multi-grade course configuration (array of grade/subject/totalXp with optional per-course overrides) */
4461
- courses: TimebackCourseConfigWithOverrides[];
4462
- /** Optional base configuration (shared across all courses, can be overridden per-course) */
4463
- base?: TimebackBaseConfig;
4464
- }
4465
- /**
4466
- * Custom API routes integration
4467
- */
4468
- interface CustomRoutesIntegration {
4469
- /** Directory for custom API routes (defaults to 'server/api') */
4470
- directory?: string;
4471
- }
4472
- /**
4473
- * Database integration
4474
- */
4475
- interface DatabaseIntegration {
4476
- /** Database directory (defaults to 'db') */
4477
- directory?: string;
4478
- /** Schema strategy: 'push' uses drizzle-kit push-style diffing, 'migrate' uses migration files.
4479
- * When omitted, auto-detects based on presence of a migrations directory with _journal.json. */
4480
- strategy?: 'push' | 'migrate';
4481
- }
4482
- interface QueueConfig {
4483
- maxBatchSize?: number;
4484
- maxRetries?: number;
4485
- maxBatchTimeout?: number;
4486
- maxConcurrency?: number;
4487
- retryDelay?: number;
4488
- deadLetterQueue?: string;
4489
- }
4490
- /**
4491
- * Integrations configuration
4492
- * All backend features (database, custom routes, external services) are configured here
4493
- */
4494
- interface IntegrationsConfig {
4495
- /** TimeBack integration (optional) */
4496
- timeback?: TimebackIntegrationConfig | null;
4497
- /** Custom API routes (optional) */
4498
- customRoutes?: CustomRoutesIntegration | boolean;
4499
- /** Database (optional) */
4500
- database?: DatabaseIntegration | boolean;
4501
- /** Key-Value storage (optional) */
4502
- kv?: boolean;
4503
- /** Bucket storage (optional) */
4504
- bucket?: boolean;
4505
- /** Authentication (optional) */
4506
- auth?: boolean;
4507
- /** Queues (optional) */
4508
- queues?: Record<string, QueueConfig | boolean>;
4509
- }
4510
- /**
4511
- * Unified Playcademy configuration
4512
- * Used for playcademy.config.{js,json}
4513
- */
4514
- interface PlaycademyConfig {
4515
- /** Game name */
4516
- name: string;
4517
- /** Game description */
4518
- description?: string;
4519
- /** Game emoji icon */
4520
- emoji?: string;
4521
- /** Build command to run before deployment */
4522
- buildCommand?: string[];
4523
- /** Path to build output */
4524
- buildPath?: string;
4525
- /** Game type */
4526
- gameType?: 'hosted' | 'external';
4527
- /** External URL (for external games) */
4528
- externalUrl?: string;
4529
- /** Game platform */
4530
- platform?: 'web' | 'unity' | 'godot';
4531
- /** Integrations (database, custom routes, external services) */
4532
- integrations?: IntegrationsConfig;
4533
- }
4534
-
4535
- /**
4536
- * Configuration options for initializing a PlaycademyClient instance.
4537
- *
4538
- * @example
4539
- * ```typescript
4540
- * const config: PlaycademyServerClientConfig = {
4541
- * apiKey: process.env.PLAYCADEMY_API_KEY!,
4542
- * gameId: 'my-math-game',
4543
- * configPath: './playcademy.config.js'
4544
- * }
4545
- * ```
4546
- */
4547
- interface PlaycademyServerClientConfig {
4548
- /**
4549
- * Playcademy API key for server-to-server authentication.
4550
- * Obtain from the Playcademy developer dashboard.
4551
- */
4552
- apiKey: string;
4553
- /**
4554
- * Optional path to playcademy.config.js file.
4555
- * If not provided, searches current directory and up to 3 parent directories.
4556
- * Ignored if `config` is provided directly.
4557
- *
4558
- * @example './config/playcademy.config.js'
4559
- */
4560
- configPath?: string;
4561
- /**
4562
- * Optional config object (for edge environments without filesystem).
4563
- * If provided, skips filesystem-based config loading.
4564
- *
4565
- * @example { name: 'My Game', integrations: { timeback: {...} } }
4566
- */
4567
- config?: PlaycademyConfig;
4568
- /**
4569
- * Optional base URL for Playcademy API.
4570
- * Defaults to environment variables or 'https://hub.playcademy.net'.
4571
- *
4572
- * @example 'http://localhost:3000' for local development
4573
- */
4574
- baseUrl?: string;
4575
- /**
4576
- * Optional game ID.
4577
- * If not provided, will attempt to fetch from API using the API token.
4578
- *
4579
- * @example 'my-math-game'
4580
- */
4581
- gameId?: string;
4582
- }
4583
- /**
4584
- * Internal state maintained by the PlaycademyClient instance.
4585
- *
4586
- * @internal
4587
- */
4588
- interface PlaycademyServerClientState {
4589
- /** API key for authentication */
4590
- apiKey: string;
4591
- /** Base URL for API requests */
4592
- baseUrl: string;
4593
- /** Game identifier */
4594
- gameId: string;
4595
- /** Loaded game configuration from playcademy.config.js */
4596
- config: PlaycademyConfig;
4597
- /**
4598
- * TimeBack course ID fetched from the Playcademy API.
4599
- * Used for all TimeBack event recording.
4600
- */
4601
- courseId?: string;
4602
- }
4603
-
4604
4616
  /**
4605
4617
  * Connection monitoring types
4606
4618
  *
@@ -5241,8 +5253,8 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
5241
5253
  */
5242
5254
  runtime: {
5243
5255
  getGameToken: (gameId: string, options?: {
5244
- apply?: boolean | undefined;
5245
- } | undefined) => Promise<GameTokenResponse>;
5256
+ apply?: boolean;
5257
+ }) => Promise<GameTokenResponse>;
5246
5258
  exit: () => Promise<void>;
5247
5259
  onInit: (handler: (context: GameContextPayload) => void) => void;
5248
5260
  onTokenRefresh: (handler: (data: {
@@ -5266,7 +5278,7 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
5266
5278
  getListenerCounts: () => Record<string, number>;
5267
5279
  assets: {
5268
5280
  url(pathOrStrings: string | TemplateStringsArray, ...values: unknown[]): string;
5269
- fetch: (path: string, options?: RequestInit | undefined) => Promise<Response>;
5281
+ fetch: (path: string, options?: RequestInit) => Promise<Response>;
5270
5282
  json: <T = unknown>(path: string) => Promise<T>;
5271
5283
  blob: (path: string) => Promise<Blob>;
5272
5284
  text: (path: string) => Promise<string>;
@@ -5291,10 +5303,13 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
5291
5303
  */
5292
5304
  timeback: {
5293
5305
  readonly user: TimebackUser;
5294
- startActivity: (metadata: ActivityData, options?: StartActivityOptions | undefined) => void;
5306
+ startActivity: (metadata: ActivityData, options?: StartActivityOptions) => void;
5295
5307
  pauseActivity: () => void;
5296
5308
  resumeActivity: () => void;
5297
5309
  endActivity: (data: EndActivityScoreData) => Promise<EndActivityResponse>;
5310
+ advanceCourse: (options?: {
5311
+ subject?: TimebackSubject;
5312
+ }) => Promise<AdvanceCourseResponse>;
5298
5313
  };
5299
5314
  /**
5300
5315
  * Playcademy Credits (platform currency) management.
@@ -5311,14 +5326,14 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
5311
5326
  * - `submit(score, metadata?)` - Record a game score
5312
5327
  */
5313
5328
  scores: {
5314
- submit: (score: number, metadata?: Record<string, unknown> | undefined) => Promise<ScoreSubmission>;
5329
+ submit: (score: number, metadata?: Record<string, unknown>) => Promise<ScoreSubmission>;
5315
5330
  };
5316
5331
  /**
5317
5332
  * Read-only leaderboard access for the current game scope.
5318
5333
  * - `fetch(options?)` - Fetch leaderboard entries
5319
5334
  */
5320
5335
  leaderboard: {
5321
- fetch: (options?: LeaderboardOptions | undefined) => Promise<GameLeaderboardEntry[]>;
5336
+ fetch: (options?: LeaderboardOptions) => Promise<GameLeaderboardEntry[]>;
5322
5337
  };
5323
5338
  /**
5324
5339
  * Demo-mode helpers. Methods throw when called outside `client.mode === 'demo'`,
@@ -5332,7 +5347,7 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
5332
5347
  get: () => Promise<DemoProfile>;
5333
5348
  update: (updates: DemoProfileUpdate) => Promise<DemoProfile>;
5334
5349
  };
5335
- end: (score: number, options?: DemoEndOptions | undefined) => void;
5350
+ end: (score: number, options?: DemoEndOptions) => void;
5336
5351
  };
5337
5352
  /**
5338
5353
  * Realtime multiplayer authentication.
@@ -5349,13 +5364,13 @@ declare class PlaycademyClient extends PlaycademyBaseClient {
5349
5364
  * - Routes are relative to your game's deployment (e.g., '/hello' → your-game.playcademy.gg/api/hello)
5350
5365
  */
5351
5366
  backend: {
5352
- get<T = unknown>(path: string, headers?: Record<string, string> | undefined): Promise<T>;
5353
- post<T = unknown>(path: string, body?: unknown, headers?: Record<string, string> | undefined): Promise<T>;
5354
- put<T = unknown>(path: string, body?: unknown, headers?: Record<string, string> | undefined): Promise<T>;
5355
- patch<T = unknown>(path: string, body?: unknown, headers?: Record<string, string> | undefined): Promise<T>;
5356
- delete<T = unknown>(path: string, headers?: Record<string, string> | undefined): Promise<T>;
5357
- request<T = unknown>(path: string, method: Method, body?: unknown, headers?: Record<string, string> | undefined): Promise<T>;
5358
- download(path: string, method?: Method, body?: unknown, headers?: Record<string, string> | undefined): Promise<Response>;
5367
+ get<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
5368
+ post<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
5369
+ put<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
5370
+ patch<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
5371
+ delete<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
5372
+ request<T = unknown>(path: string, method: Method, body?: unknown, headers?: Record<string, string>): Promise<T>;
5373
+ download(path: string, method?: Method, body?: unknown, headers?: Record<string, string>): Promise<Response>;
5359
5374
  url(pathOrStrings: string | TemplateStringsArray, ...values: unknown[]): string;
5360
5375
  };
5361
5376
  /** Auto-initializes a PlaycademyClient with context from the environment */