@playcademy/sdk 0.1.13 → 0.1.15

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/index.d.ts CHANGED
@@ -1,12 +1,250 @@
1
- import * as _playcademy_realtime_server_types from '@playcademy/realtime/server/types';
1
+ import { AUTH_PROVIDER_IDS } from '@playcademy/constants';
2
2
  import { InferSelectModel } from 'drizzle-orm';
3
3
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
4
4
  import * as drizzle_zod from 'drizzle-zod';
5
5
  import { z } from 'zod';
6
+ import * as _playcademy_realtime_server_types from '@playcademy/realtime/server/types';
6
7
  import * as _playcademy_timeback_types from '@playcademy/timeback/types';
7
8
  import { OrganizationConfig, CourseConfig, ComponentConfig, ResourceConfig, ComponentResourceConfig } from '@playcademy/timeback/types';
8
9
  import { SchemaInfo } from '@playcademy/cloudflare';
9
- import { AUTH_PROVIDER_IDS } from '@playcademy/constants';
10
+
11
+ /**
12
+ * Base error class for Cademy SDK specific errors.
13
+ */
14
+ declare class PlaycademyError extends Error {
15
+ constructor(message: string);
16
+ }
17
+ declare class ApiError extends Error {
18
+ status: number;
19
+ details: unknown;
20
+ constructor(status: number, message: string, details: unknown);
21
+ }
22
+ /**
23
+ * Extract useful error information from an API error
24
+ * Useful for displaying errors to users in a friendly way
25
+ */
26
+ interface ApiErrorInfo {
27
+ status: number;
28
+ statusText: string;
29
+ error?: string;
30
+ message?: string;
31
+ details?: unknown;
32
+ }
33
+ declare function extractApiErrorInfo(error: unknown): ApiErrorInfo | null;
34
+
35
+ /**
36
+ * Connection monitoring types
37
+ *
38
+ * Type definitions for connection state, configuration, and callbacks.
39
+ */
40
+ /**
41
+ * Possible connection states.
42
+ *
43
+ * - **online**: Connection is stable and healthy
44
+ * - **offline**: Complete loss of network connectivity
45
+ * - **degraded**: Connection is slow or experiencing intermittent issues
46
+ */
47
+ type ConnectionState = 'online' | 'offline' | 'degraded';
48
+ /**
49
+ * Configuration options for ConnectionMonitor.
50
+ *
51
+ * @see {@link ConnectionMonitor} for usage
52
+ */
53
+ interface ConnectionMonitorConfig {
54
+ /** Base URL for heartbeat pings (e.g., 'https://api.playcademy.com') */
55
+ baseUrl: string;
56
+ /** How often to send heartbeat pings in milliseconds (default: 10000) */
57
+ heartbeatInterval?: number;
58
+ /** How long to wait for heartbeat response in milliseconds (default: 5000) */
59
+ heartbeatTimeout?: number;
60
+ /** Number of consecutive failures before triggering disconnect (default: 2) */
61
+ failureThreshold?: number;
62
+ /** Enable periodic heartbeat monitoring (default: true) */
63
+ enableHeartbeat?: boolean;
64
+ /** Enable browser online/offline event listeners (default: true) */
65
+ enableOfflineEvents?: boolean;
66
+ }
67
+ /**
68
+ * Callback function signature for connection state changes.
69
+ *
70
+ * @param state - The new connection state
71
+ * @param reason - Human-readable reason for the state change
72
+ */
73
+ type ConnectionChangeCallback = (state: ConnectionState, reason: string) => void;
74
+
75
+ /**
76
+ * Connection Monitor
77
+ *
78
+ * Monitors network connectivity using multiple signals:
79
+ * 1. navigator.onLine - Instant offline detection
80
+ * 2. Periodic heartbeat - Detects slow/degraded connections
81
+ * 3. Request failure tracking - Piggybacks on actual API calls
82
+ *
83
+ * Designed for school WiFi environments where connections may be
84
+ * unstable or degraded without fully disconnecting.
85
+ */
86
+
87
+ /**
88
+ * Monitors network connectivity using multiple signals and notifies callbacks of state changes.
89
+ *
90
+ * The ConnectionMonitor uses a multi-signal approach to detect connection issues:
91
+ *
92
+ * 1. **navigator.onLine events** - Instant detection of hard disconnects
93
+ * 2. **Heartbeat pings** - Periodic checks to detect slow/degraded connections
94
+ * 3. **Request failure tracking** - Piggybacks on actual API calls
95
+ *
96
+ * This comprehensive approach ensures reliable detection across different network
97
+ * failure modes common in school WiFi environments (hard disconnect, slow connection,
98
+ * intermittent failures).
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * const monitor = new ConnectionMonitor({
103
+ * baseUrl: 'https://api.playcademy.com',
104
+ * heartbeatInterval: 10000, // Check every 10s
105
+ * failureThreshold: 2 // Trigger after 2 failures
106
+ * })
107
+ *
108
+ * monitor.onChange((state, reason) => {
109
+ * console.log(`Connection: ${state} - ${reason}`)
110
+ * })
111
+ *
112
+ * monitor.start()
113
+ * ```
114
+ *
115
+ * @see {@link ConnectionManagerConfig} for configuration options
116
+ */
117
+ declare class ConnectionMonitor {
118
+ private state;
119
+ private callbacks;
120
+ private heartbeatInterval?;
121
+ private consecutiveFailures;
122
+ private isMonitoring;
123
+ private config;
124
+ /**
125
+ * Creates a new ConnectionMonitor instance.
126
+ *
127
+ * The monitor starts in a stopped state. Call `start()` to begin monitoring.
128
+ *
129
+ * @param config - Configuration options
130
+ * @param config.baseUrl - Base URL for heartbeat pings
131
+ * @param config.heartbeatInterval - How often to check (default: 10000ms)
132
+ * @param config.heartbeatTimeout - Request timeout (default: 5000ms)
133
+ * @param config.failureThreshold - Failures before triggering disconnect (default: 2)
134
+ * @param config.enableHeartbeat - Enable periodic checks (default: true)
135
+ * @param config.enableOfflineEvents - Listen to browser events (default: true)
136
+ */
137
+ constructor(config: ConnectionMonitorConfig);
138
+ /**
139
+ * Starts monitoring the connection state.
140
+ *
141
+ * Sets up event listeners and begins heartbeat checks based on configuration.
142
+ * Idempotent - safe to call multiple times.
143
+ */
144
+ start(): void;
145
+ /**
146
+ * Stops monitoring the connection state and cleans up resources.
147
+ *
148
+ * Removes event listeners and clears heartbeat intervals.
149
+ * Idempotent - safe to call multiple times.
150
+ */
151
+ stop(): void;
152
+ /**
153
+ * Registers a callback to be notified of all connection state changes.
154
+ *
155
+ * The callback fires for all state transitions: online → offline,
156
+ * offline → degraded, degraded → online, etc.
157
+ *
158
+ * @param callback - Function called with (state, reason) when connection changes
159
+ * @returns Cleanup function to unregister the callback
160
+ *
161
+ * @example
162
+ * ```typescript
163
+ * const cleanup = monitor.onChange((state, reason) => {
164
+ * console.log(`Connection: ${state}`)
165
+ * if (state === 'offline') {
166
+ * showReconnectingUI()
167
+ * }
168
+ * })
169
+ *
170
+ * // Later: cleanup() to unregister
171
+ * ```
172
+ */
173
+ onChange(callback: ConnectionChangeCallback): () => void;
174
+ /**
175
+ * Gets the current connection state.
176
+ *
177
+ * @returns The current state ('online', 'offline', or 'degraded')
178
+ */
179
+ getState(): ConnectionState;
180
+ /**
181
+ * Manually triggers an immediate connection check.
182
+ *
183
+ * Forces a heartbeat ping to verify connectivity right now, bypassing
184
+ * the normal interval. Useful before critical operations.
185
+ *
186
+ * @returns Promise resolving to the current connection state after the check
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * const state = await monitor.checkNow()
191
+ * if (state !== 'online') {
192
+ * alert('Please check your internet connection')
193
+ * }
194
+ * ```
195
+ */
196
+ checkNow(): Promise<ConnectionState>;
197
+ /**
198
+ * Reports a request failure for tracking.
199
+ *
200
+ * This should be called from your request wrapper whenever an API call fails.
201
+ * Only network errors are tracked (TypeError, fetch failures) - HTTP error
202
+ * responses (4xx, 5xx) are ignored.
203
+ *
204
+ * After consecutive failures exceed the threshold, the monitor transitions
205
+ * to 'degraded' or 'offline' state.
206
+ *
207
+ * @param error - The error from the failed request
208
+ *
209
+ * @example
210
+ * ```typescript
211
+ * try {
212
+ * await fetch('/api/data')
213
+ * } catch (error) {
214
+ * monitor.reportRequestFailure(error)
215
+ * throw error
216
+ * }
217
+ * ```
218
+ */
219
+ reportRequestFailure(error: unknown): void;
220
+ /**
221
+ * Reports a successful request.
222
+ *
223
+ * This should be called from your request wrapper whenever an API call succeeds.
224
+ * Resets the consecutive failure counter and transitions from 'degraded' to
225
+ * 'online' if the connection has recovered.
226
+ *
227
+ * @example
228
+ * ```typescript
229
+ * try {
230
+ * const result = await fetch('/api/data')
231
+ * monitor.reportRequestSuccess()
232
+ * return result
233
+ * } catch (error) {
234
+ * monitor.reportRequestFailure(error)
235
+ * throw error
236
+ * }
237
+ * ```
238
+ */
239
+ reportRequestSuccess(): void;
240
+ private _detectInitialState;
241
+ private _handleOnline;
242
+ private _handleOffline;
243
+ private _startHeartbeat;
244
+ private _performHeartbeat;
245
+ private _handleHeartbeatFailure;
246
+ private _setState;
247
+ }
10
248
 
11
249
  declare const users: drizzle_orm_pg_core.PgTableWithColumns<{
12
250
  name: "user";
@@ -225,6 +463,23 @@ type GameMetadata = {
225
463
  emoji?: string;
226
464
  [key: string]: unknown;
227
465
  };
466
+ /**
467
+ * DNS validation records for custom hostname
468
+ * Structure for the validationRecords JSON field in game_custom_hostnames table
469
+ */
470
+ type CustomHostnameValidationRecords = {
471
+ /** TXT record for ownership verification */
472
+ ownership?: {
473
+ name?: string;
474
+ value?: string;
475
+ type?: string;
476
+ };
477
+ /** TXT records for SSL certificate validation */
478
+ ssl?: Array<{
479
+ txt_name?: string;
480
+ txt_value?: string;
481
+ }>;
482
+ };
228
483
  declare const games: drizzle_orm_pg_core.PgTableWithColumns<{
229
484
  name: "games";
230
485
  schema: undefined;
@@ -337,8 +592,8 @@ declare const games: drizzle_orm_pg_core.PgTableWithColumns<{
337
592
  identity: undefined;
338
593
  generated: undefined;
339
594
  }, {}, {}>;
340
- assetBundleBase: drizzle_orm_pg_core.PgColumn<{
341
- name: "asset_bundle_base";
595
+ deploymentUrl: drizzle_orm_pg_core.PgColumn<{
596
+ name: "deployment_url";
342
597
  tableName: "games";
343
598
  dataType: "string";
344
599
  columnType: "PgText";
@@ -461,13 +716,18 @@ declare const games: drizzle_orm_pg_core.PgTableWithColumns<{
461
716
  };
462
717
  dialect: "pg";
463
718
  }>;
464
- declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
465
- name: "items";
719
+ /**
720
+ * Custom hostnames table
721
+ *
722
+ * Stores custom domain mappings for games with SSL provisioning via Cloudflare.
723
+ */
724
+ declare const gameCustomHostnames: drizzle_orm_pg_core.PgTableWithColumns<{
725
+ name: "game_custom_hostnames";
466
726
  schema: undefined;
467
727
  columns: {
468
728
  id: drizzle_orm_pg_core.PgColumn<{
469
729
  name: "id";
470
- tableName: "items";
730
+ tableName: "game_custom_hostnames";
471
731
  dataType: "string";
472
732
  columnType: "PgUUID";
473
733
  data: string;
@@ -482,11 +742,11 @@ declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
482
742
  identity: undefined;
483
743
  generated: undefined;
484
744
  }, {}, {}>;
485
- slug: drizzle_orm_pg_core.PgColumn<{
486
- name: "slug";
487
- tableName: "items";
745
+ gameId: drizzle_orm_pg_core.PgColumn<{
746
+ name: "game_id";
747
+ tableName: "game_custom_hostnames";
488
748
  dataType: "string";
489
- columnType: "PgText";
749
+ columnType: "PgUUID";
490
750
  data: string;
491
751
  driverParam: string;
492
752
  notNull: true;
@@ -494,31 +754,31 @@ declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
494
754
  isPrimaryKey: false;
495
755
  isAutoincrement: false;
496
756
  hasRuntimeDefault: false;
497
- enumValues: [string, ...string[]];
757
+ enumValues: undefined;
498
758
  baseColumn: never;
499
759
  identity: undefined;
500
760
  generated: undefined;
501
761
  }, {}, {}>;
502
- gameId: drizzle_orm_pg_core.PgColumn<{
503
- name: "game_id";
504
- tableName: "items";
762
+ userId: drizzle_orm_pg_core.PgColumn<{
763
+ name: "user_id";
764
+ tableName: "game_custom_hostnames";
505
765
  dataType: "string";
506
- columnType: "PgUUID";
766
+ columnType: "PgText";
507
767
  data: string;
508
768
  driverParam: string;
509
- notNull: false;
769
+ notNull: true;
510
770
  hasDefault: false;
511
771
  isPrimaryKey: false;
512
772
  isAutoincrement: false;
513
773
  hasRuntimeDefault: false;
514
- enumValues: undefined;
774
+ enumValues: [string, ...string[]];
515
775
  baseColumn: never;
516
776
  identity: undefined;
517
777
  generated: undefined;
518
778
  }, {}, {}>;
519
- displayName: drizzle_orm_pg_core.PgColumn<{
520
- name: "display_name";
521
- tableName: "items";
779
+ hostname: drizzle_orm_pg_core.PgColumn<{
780
+ name: "hostname";
781
+ tableName: "game_custom_hostnames";
522
782
  dataType: "string";
523
783
  columnType: "PgText";
524
784
  data: string;
@@ -533,14 +793,14 @@ declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
533
793
  identity: undefined;
534
794
  generated: undefined;
535
795
  }, {}, {}>;
536
- description: drizzle_orm_pg_core.PgColumn<{
537
- name: "description";
538
- tableName: "items";
796
+ cloudflareId: drizzle_orm_pg_core.PgColumn<{
797
+ name: "cloudflare_id";
798
+ tableName: "game_custom_hostnames";
539
799
  dataType: "string";
540
800
  columnType: "PgText";
541
801
  data: string;
542
802
  driverParam: string;
543
- notNull: false;
803
+ notNull: true;
544
804
  hasDefault: false;
545
805
  isPrimaryKey: false;
546
806
  isAutoincrement: false;
@@ -550,66 +810,66 @@ declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
550
810
  identity: undefined;
551
811
  generated: undefined;
552
812
  }, {}, {}>;
553
- type: drizzle_orm_pg_core.PgColumn<{
554
- name: "type";
555
- tableName: "items";
813
+ environment: drizzle_orm_pg_core.PgColumn<{
814
+ name: "environment";
815
+ tableName: "game_custom_hostnames";
556
816
  dataType: "string";
557
817
  columnType: "PgEnumColumn";
558
- data: "accessory" | "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "other";
818
+ data: "staging" | "production";
559
819
  driverParam: string;
560
820
  notNull: true;
561
821
  hasDefault: true;
562
822
  isPrimaryKey: false;
563
823
  isAutoincrement: false;
564
824
  hasRuntimeDefault: false;
565
- enumValues: ["currency", "badge", "trophy", "collectible", "consumable", "unlock", "upgrade", "accessory", "other"];
825
+ enumValues: ["staging", "production"];
566
826
  baseColumn: never;
567
827
  identity: undefined;
568
828
  generated: undefined;
569
829
  }, {}, {}>;
570
- isPlaceable: drizzle_orm_pg_core.PgColumn<{
571
- name: "is_placeable";
572
- tableName: "items";
573
- dataType: "boolean";
574
- columnType: "PgBoolean";
575
- data: boolean;
576
- driverParam: boolean;
830
+ status: drizzle_orm_pg_core.PgColumn<{
831
+ name: "status";
832
+ tableName: "game_custom_hostnames";
833
+ dataType: "string";
834
+ columnType: "PgEnumColumn";
835
+ data: "pending" | "pending_validation" | "pending_deployment" | "pending_deletion" | "active" | "blocked" | "deleted";
836
+ driverParam: string;
577
837
  notNull: true;
578
838
  hasDefault: true;
579
839
  isPrimaryKey: false;
580
840
  isAutoincrement: false;
581
841
  hasRuntimeDefault: false;
582
- enumValues: undefined;
842
+ enumValues: ["pending", "pending_validation", "pending_deployment", "pending_deletion", "active", "blocked", "deleted"];
583
843
  baseColumn: never;
584
844
  identity: undefined;
585
845
  generated: undefined;
586
846
  }, {}, {}>;
587
- imageUrl: drizzle_orm_pg_core.PgColumn<{
588
- name: "image_url";
589
- tableName: "items";
847
+ sslStatus: drizzle_orm_pg_core.PgColumn<{
848
+ name: "ssl_status";
849
+ tableName: "game_custom_hostnames";
590
850
  dataType: "string";
591
- columnType: "PgText";
592
- data: string;
851
+ columnType: "PgEnumColumn";
852
+ data: "pending_validation" | "pending_deployment" | "active" | "deleted" | "initializing" | "pending_issuance";
593
853
  driverParam: string;
594
- notNull: false;
595
- hasDefault: false;
854
+ notNull: true;
855
+ hasDefault: true;
596
856
  isPrimaryKey: false;
597
857
  isAutoincrement: false;
598
858
  hasRuntimeDefault: false;
599
- enumValues: [string, ...string[]];
859
+ enumValues: ["initializing", "pending_validation", "pending_issuance", "pending_deployment", "active", "deleted"];
600
860
  baseColumn: never;
601
861
  identity: undefined;
602
862
  generated: undefined;
603
863
  }, {}, {}>;
604
- metadata: drizzle_orm_pg_core.PgColumn<{
605
- name: "metadata";
606
- tableName: "items";
864
+ validationRecords: drizzle_orm_pg_core.PgColumn<{
865
+ name: "validation_records";
866
+ tableName: "game_custom_hostnames";
607
867
  dataType: "json";
608
868
  columnType: "PgJsonb";
609
- data: unknown;
869
+ data: CustomHostnameValidationRecords;
610
870
  driverParam: unknown;
611
871
  notNull: false;
612
- hasDefault: true;
872
+ hasDefault: false;
613
873
  isPrimaryKey: false;
614
874
  isAutoincrement: false;
615
875
  hasRuntimeDefault: false;
@@ -617,10 +877,29 @@ declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
617
877
  baseColumn: never;
618
878
  identity: undefined;
619
879
  generated: undefined;
620
- }, {}, {}>;
880
+ }, {}, {
881
+ $type: CustomHostnameValidationRecords;
882
+ }>;
621
883
  createdAt: drizzle_orm_pg_core.PgColumn<{
622
884
  name: "created_at";
623
- tableName: "items";
885
+ tableName: "game_custom_hostnames";
886
+ dataType: "date";
887
+ columnType: "PgTimestamp";
888
+ data: Date;
889
+ driverParam: string;
890
+ notNull: true;
891
+ hasDefault: true;
892
+ isPrimaryKey: false;
893
+ isAutoincrement: false;
894
+ hasRuntimeDefault: false;
895
+ enumValues: undefined;
896
+ baseColumn: never;
897
+ identity: undefined;
898
+ generated: undefined;
899
+ }, {}, {}>;
900
+ updatedAt: drizzle_orm_pg_core.PgColumn<{
901
+ name: "updated_at";
902
+ tableName: "game_custom_hostnames";
624
903
  dataType: "date";
625
904
  columnType: "PgTimestamp";
626
905
  data: Date;
@@ -638,13 +917,13 @@ declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
638
917
  };
639
918
  dialect: "pg";
640
919
  }>;
641
- declare const inventoryItems: drizzle_orm_pg_core.PgTableWithColumns<{
642
- name: "inventory_items";
920
+ declare const items: drizzle_orm_pg_core.PgTableWithColumns<{
921
+ name: "items";
643
922
  schema: undefined;
644
923
  columns: {
645
924
  id: drizzle_orm_pg_core.PgColumn<{
646
925
  name: "id";
647
- tableName: "inventory_items";
926
+ tableName: "items";
648
927
  dataType: "string";
649
928
  columnType: "PgUUID";
650
929
  data: string;
@@ -659,9 +938,9 @@ declare const inventoryItems: drizzle_orm_pg_core.PgTableWithColumns<{
659
938
  identity: undefined;
660
939
  generated: undefined;
661
940
  }, {}, {}>;
662
- userId: drizzle_orm_pg_core.PgColumn<{
663
- name: "user_id";
664
- tableName: "inventory_items";
941
+ slug: drizzle_orm_pg_core.PgColumn<{
942
+ name: "slug";
943
+ tableName: "items";
665
944
  dataType: "string";
666
945
  columnType: "PgText";
667
946
  data: string;
@@ -676,14 +955,14 @@ declare const inventoryItems: drizzle_orm_pg_core.PgTableWithColumns<{
676
955
  identity: undefined;
677
956
  generated: undefined;
678
957
  }, {}, {}>;
679
- itemId: drizzle_orm_pg_core.PgColumn<{
680
- name: "item_id";
681
- tableName: "inventory_items";
958
+ gameId: drizzle_orm_pg_core.PgColumn<{
959
+ name: "game_id";
960
+ tableName: "items";
682
961
  dataType: "string";
683
962
  columnType: "PgUUID";
684
963
  data: string;
685
964
  driverParam: string;
686
- notNull: true;
965
+ notNull: false;
687
966
  hasDefault: false;
688
967
  isPrimaryKey: false;
689
968
  isAutoincrement: false;
@@ -693,50 +972,227 @@ declare const inventoryItems: drizzle_orm_pg_core.PgTableWithColumns<{
693
972
  identity: undefined;
694
973
  generated: undefined;
695
974
  }, {}, {}>;
696
- quantity: drizzle_orm_pg_core.PgColumn<{
697
- name: "quantity";
698
- tableName: "inventory_items";
699
- dataType: "number";
700
- columnType: "PgInteger";
701
- data: number;
702
- driverParam: string | number;
975
+ displayName: drizzle_orm_pg_core.PgColumn<{
976
+ name: "display_name";
977
+ tableName: "items";
978
+ dataType: "string";
979
+ columnType: "PgText";
980
+ data: string;
981
+ driverParam: string;
703
982
  notNull: true;
704
- hasDefault: true;
983
+ hasDefault: false;
705
984
  isPrimaryKey: false;
706
985
  isAutoincrement: false;
707
986
  hasRuntimeDefault: false;
708
- enumValues: undefined;
987
+ enumValues: [string, ...string[]];
709
988
  baseColumn: never;
710
989
  identity: undefined;
711
990
  generated: undefined;
712
991
  }, {}, {}>;
713
- updatedAt: drizzle_orm_pg_core.PgColumn<{
714
- name: "updated_at";
715
- tableName: "inventory_items";
716
- dataType: "date";
717
- columnType: "PgTimestamp";
718
- data: Date;
992
+ description: drizzle_orm_pg_core.PgColumn<{
993
+ name: "description";
994
+ tableName: "items";
995
+ dataType: "string";
996
+ columnType: "PgText";
997
+ data: string;
719
998
  driverParam: string;
720
999
  notNull: false;
721
- hasDefault: true;
1000
+ hasDefault: false;
722
1001
  isPrimaryKey: false;
723
1002
  isAutoincrement: false;
724
1003
  hasRuntimeDefault: false;
725
- enumValues: undefined;
1004
+ enumValues: [string, ...string[]];
726
1005
  baseColumn: never;
727
1006
  identity: undefined;
728
1007
  generated: undefined;
729
1008
  }, {}, {}>;
730
- };
731
- dialect: "pg";
732
- }>;
733
- declare const shopListings: drizzle_orm_pg_core.PgTableWithColumns<{
734
- name: "shop_listings";
735
- schema: undefined;
736
- columns: {
737
- id: drizzle_orm_pg_core.PgColumn<{
738
- name: "id";
739
- tableName: "shop_listings";
1009
+ type: drizzle_orm_pg_core.PgColumn<{
1010
+ name: "type";
1011
+ tableName: "items";
1012
+ dataType: "string";
1013
+ columnType: "PgEnumColumn";
1014
+ data: "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "accessory" | "other";
1015
+ driverParam: string;
1016
+ notNull: true;
1017
+ hasDefault: true;
1018
+ isPrimaryKey: false;
1019
+ isAutoincrement: false;
1020
+ hasRuntimeDefault: false;
1021
+ enumValues: ["currency", "badge", "trophy", "collectible", "consumable", "unlock", "upgrade", "accessory", "other"];
1022
+ baseColumn: never;
1023
+ identity: undefined;
1024
+ generated: undefined;
1025
+ }, {}, {}>;
1026
+ isPlaceable: drizzle_orm_pg_core.PgColumn<{
1027
+ name: "is_placeable";
1028
+ tableName: "items";
1029
+ dataType: "boolean";
1030
+ columnType: "PgBoolean";
1031
+ data: boolean;
1032
+ driverParam: boolean;
1033
+ notNull: true;
1034
+ hasDefault: true;
1035
+ isPrimaryKey: false;
1036
+ isAutoincrement: false;
1037
+ hasRuntimeDefault: false;
1038
+ enumValues: undefined;
1039
+ baseColumn: never;
1040
+ identity: undefined;
1041
+ generated: undefined;
1042
+ }, {}, {}>;
1043
+ imageUrl: drizzle_orm_pg_core.PgColumn<{
1044
+ name: "image_url";
1045
+ tableName: "items";
1046
+ dataType: "string";
1047
+ columnType: "PgText";
1048
+ data: string;
1049
+ driverParam: string;
1050
+ notNull: false;
1051
+ hasDefault: false;
1052
+ isPrimaryKey: false;
1053
+ isAutoincrement: false;
1054
+ hasRuntimeDefault: false;
1055
+ enumValues: [string, ...string[]];
1056
+ baseColumn: never;
1057
+ identity: undefined;
1058
+ generated: undefined;
1059
+ }, {}, {}>;
1060
+ metadata: drizzle_orm_pg_core.PgColumn<{
1061
+ name: "metadata";
1062
+ tableName: "items";
1063
+ dataType: "json";
1064
+ columnType: "PgJsonb";
1065
+ data: unknown;
1066
+ driverParam: unknown;
1067
+ notNull: false;
1068
+ hasDefault: true;
1069
+ isPrimaryKey: false;
1070
+ isAutoincrement: false;
1071
+ hasRuntimeDefault: false;
1072
+ enumValues: undefined;
1073
+ baseColumn: never;
1074
+ identity: undefined;
1075
+ generated: undefined;
1076
+ }, {}, {}>;
1077
+ createdAt: drizzle_orm_pg_core.PgColumn<{
1078
+ name: "created_at";
1079
+ tableName: "items";
1080
+ dataType: "date";
1081
+ columnType: "PgTimestamp";
1082
+ data: Date;
1083
+ driverParam: string;
1084
+ notNull: true;
1085
+ hasDefault: true;
1086
+ isPrimaryKey: false;
1087
+ isAutoincrement: false;
1088
+ hasRuntimeDefault: false;
1089
+ enumValues: undefined;
1090
+ baseColumn: never;
1091
+ identity: undefined;
1092
+ generated: undefined;
1093
+ }, {}, {}>;
1094
+ };
1095
+ dialect: "pg";
1096
+ }>;
1097
+ declare const inventoryItems: drizzle_orm_pg_core.PgTableWithColumns<{
1098
+ name: "inventory_items";
1099
+ schema: undefined;
1100
+ columns: {
1101
+ id: drizzle_orm_pg_core.PgColumn<{
1102
+ name: "id";
1103
+ tableName: "inventory_items";
1104
+ dataType: "string";
1105
+ columnType: "PgUUID";
1106
+ data: string;
1107
+ driverParam: string;
1108
+ notNull: true;
1109
+ hasDefault: true;
1110
+ isPrimaryKey: true;
1111
+ isAutoincrement: false;
1112
+ hasRuntimeDefault: false;
1113
+ enumValues: undefined;
1114
+ baseColumn: never;
1115
+ identity: undefined;
1116
+ generated: undefined;
1117
+ }, {}, {}>;
1118
+ userId: drizzle_orm_pg_core.PgColumn<{
1119
+ name: "user_id";
1120
+ tableName: "inventory_items";
1121
+ dataType: "string";
1122
+ columnType: "PgText";
1123
+ data: string;
1124
+ driverParam: string;
1125
+ notNull: true;
1126
+ hasDefault: false;
1127
+ isPrimaryKey: false;
1128
+ isAutoincrement: false;
1129
+ hasRuntimeDefault: false;
1130
+ enumValues: [string, ...string[]];
1131
+ baseColumn: never;
1132
+ identity: undefined;
1133
+ generated: undefined;
1134
+ }, {}, {}>;
1135
+ itemId: drizzle_orm_pg_core.PgColumn<{
1136
+ name: "item_id";
1137
+ tableName: "inventory_items";
1138
+ dataType: "string";
1139
+ columnType: "PgUUID";
1140
+ data: string;
1141
+ driverParam: string;
1142
+ notNull: true;
1143
+ hasDefault: false;
1144
+ isPrimaryKey: false;
1145
+ isAutoincrement: false;
1146
+ hasRuntimeDefault: false;
1147
+ enumValues: undefined;
1148
+ baseColumn: never;
1149
+ identity: undefined;
1150
+ generated: undefined;
1151
+ }, {}, {}>;
1152
+ quantity: drizzle_orm_pg_core.PgColumn<{
1153
+ name: "quantity";
1154
+ tableName: "inventory_items";
1155
+ dataType: "number";
1156
+ columnType: "PgInteger";
1157
+ data: number;
1158
+ driverParam: string | number;
1159
+ notNull: true;
1160
+ hasDefault: true;
1161
+ isPrimaryKey: false;
1162
+ isAutoincrement: false;
1163
+ hasRuntimeDefault: false;
1164
+ enumValues: undefined;
1165
+ baseColumn: never;
1166
+ identity: undefined;
1167
+ generated: undefined;
1168
+ }, {}, {}>;
1169
+ updatedAt: drizzle_orm_pg_core.PgColumn<{
1170
+ name: "updated_at";
1171
+ tableName: "inventory_items";
1172
+ dataType: "date";
1173
+ columnType: "PgTimestamp";
1174
+ data: Date;
1175
+ driverParam: string;
1176
+ notNull: false;
1177
+ hasDefault: true;
1178
+ isPrimaryKey: false;
1179
+ isAutoincrement: false;
1180
+ hasRuntimeDefault: false;
1181
+ enumValues: undefined;
1182
+ baseColumn: never;
1183
+ identity: undefined;
1184
+ generated: undefined;
1185
+ }, {}, {}>;
1186
+ };
1187
+ dialect: "pg";
1188
+ }>;
1189
+ declare const shopListings: drizzle_orm_pg_core.PgTableWithColumns<{
1190
+ name: "shop_listings";
1191
+ schema: undefined;
1192
+ columns: {
1193
+ id: drizzle_orm_pg_core.PgColumn<{
1194
+ name: "id";
1195
+ tableName: "shop_listings";
740
1196
  dataType: "string";
741
1197
  columnType: "PgUUID";
742
1198
  data: string;
@@ -1442,7 +1898,7 @@ declare const characterComponents: drizzle_orm_pg_core.PgTableWithColumns<{
1442
1898
  tableName: "character_components";
1443
1899
  dataType: "string";
1444
1900
  columnType: "PgEnumColumn";
1445
- data: "body" | "outfit" | "hairstyle" | "eyes" | "accessory";
1901
+ data: "accessory" | "body" | "outfit" | "hairstyle" | "eyes";
1446
1902
  driverParam: string;
1447
1903
  notNull: true;
1448
1904
  hasDefault: false;
@@ -2292,66 +2748,32 @@ declare const UpsertGameMetadataSchema: z.ZodEffects<z.ZodObject<{
2292
2748
  mapElementId?: string | null | undefined;
2293
2749
  metadata?: Record<string, unknown> | undefined;
2294
2750
  }>;
2751
+ /**
2752
+ * Simplified game manifest for Cloudflare Workers deployments
2753
+ *
2754
+ * The manifest is auto-generated by build tools (Godot plugin, Vite plugin, etc.)
2755
+ * and provides minimal metadata about the build.
2756
+ *
2757
+ * Fields:
2758
+ * - version: Manifest schema version (for future migrations)
2759
+ * - platform: Auto-detected by build tool (e.g., 'web', 'godot@4.3', 'unity@2023')
2760
+ * - createdAt: Build timestamp (ISO 8601)
2761
+ *
2762
+ * Note: With unified Cloudflare Workers deployments, the worker handles all routing
2763
+ * internally, so we no longer need bootMode, entryPoint, or styles fields.
2764
+ */
2295
2765
  declare const ManifestV1Schema: z.ZodObject<{
2296
2766
  version: z.ZodString;
2297
- bootMode: z.ZodEnum<["iframe", "module"]>;
2298
- entryPoint: z.ZodEffects<z.ZodString, string, string>;
2299
- /**
2300
- * An array of relative paths to CSS stylesheets.
2301
- * Currently, this property is ONLY utilized by the 'module' boot mode
2302
- * (`apps/cademy/src/lib/loader/boot-module.ts`) to load external
2303
- * stylesheets into the game's shadow DOM.
2304
- *
2305
- * This property is under review for potential deprecation and removal.
2306
- * The 'module' boot mode itself has not yet seen significant adoption
2307
- * or clear use-cases, and if support for module loading is removed in
2308
- * the future, this 'styles' property would become obsolete.
2309
- *
2310
- * If module-based games need styling, alternative approaches might involve
2311
- * bundling CSS within their JavaScript or managing style injection internally.
2312
- */
2313
- styles: z.ZodEffects<z.ZodOptional<z.ZodArray<z.ZodString, "many">>, string[] | undefined, string[] | undefined>;
2314
- /**
2315
- * Specifies the game or development platform.
2316
- * Current values include 'web', 'godot', and 'unity'.
2317
- * The default in the database is 'web'.
2318
- *
2319
- * IMPORTANT: This property is NOT CURRENTLY USED by the core loader
2320
- * or runtime systems in any functional way. It is present in the manifest
2321
- * but does not drive any specific behavior in `boot-iframe.ts` or
2322
- * `boot-module.ts` beyond being a piece of metadata.
2323
- *
2324
- * Potential Future Uses (Speculative):
2325
- * - Analytics: Could be used to gather stats on platform popularity/usage.
2326
- * - Developer Portal: Might inform UI/UX in the dev portal, e.g., showing
2327
- * platform-specific guides or settings.
2328
- * - Loader Optimizations: In the future, the loader could potentially use this
2329
- * to apply platform-specific loading strategies or workarounds, though this
2330
- * is unlikely given the current generic boot process.
2331
- * - Asset Pipeline: Could hint to future asset pipeline tools about expected
2332
- * project structures or build artifacts for different platforms.
2333
- *
2334
- * The field is intentionally kept somewhat generic. If finer-grained
2335
- * distinctions are ever needed (e.g., specific web frameworks like 'phaser',
2336
- * 'three'), this schema might need to be revisited or a separate
2337
- * 'platformVersion' or 'framework' field could be added.
2338
- */
2339
- platform: z.ZodEnum<["web", "godot", "unity"]>;
2767
+ platform: z.ZodString;
2340
2768
  createdAt: z.ZodString;
2341
2769
  }, "strip", z.ZodTypeAny, {
2342
2770
  createdAt: string;
2343
2771
  version: string;
2344
- platform: "web" | "godot" | "unity";
2345
- bootMode: "iframe" | "module";
2346
- entryPoint: string;
2347
- styles?: string[] | undefined;
2772
+ platform: string;
2348
2773
  }, {
2349
2774
  createdAt: string;
2350
2775
  version: string;
2351
- platform: "web" | "godot" | "unity";
2352
- bootMode: "iframe" | "module";
2353
- entryPoint: string;
2354
- styles?: string[] | undefined;
2776
+ platform: string;
2355
2777
  }>;
2356
2778
  declare const InsertItemSchema: drizzle_zod.BuildSchema<"insert", {
2357
2779
  id: drizzle_orm_pg_core.PgColumn<{
@@ -2444,7 +2866,7 @@ declare const InsertItemSchema: drizzle_zod.BuildSchema<"insert", {
2444
2866
  tableName: "items";
2445
2867
  dataType: "string";
2446
2868
  columnType: "PgEnumColumn";
2447
- data: "accessory" | "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "other";
2869
+ data: "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "accessory" | "other";
2448
2870
  driverParam: string;
2449
2871
  notNull: true;
2450
2872
  hasDefault: true;
@@ -2540,21 +2962,21 @@ declare const UpdateItemSchema: z.ZodObject<Omit<{
2540
2962
  metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
2541
2963
  createdAt: z.ZodOptional<z.ZodDate>;
2542
2964
  }, "id" | "createdAt">, "strip", z.ZodTypeAny, {
2965
+ slug?: string | undefined;
2543
2966
  description?: string | null | undefined;
2544
2967
  displayName?: string | undefined;
2545
- slug?: string | undefined;
2546
2968
  metadata?: Record<string, unknown> | undefined;
2547
2969
  gameId?: string | null | undefined;
2548
- type?: "accessory" | "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "other" | undefined;
2970
+ type?: "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "accessory" | "other" | undefined;
2549
2971
  isPlaceable?: boolean | undefined;
2550
2972
  imageUrl?: string | null | undefined;
2551
2973
  }, {
2974
+ slug?: string | undefined;
2552
2975
  description?: string | null | undefined;
2553
2976
  displayName?: string | undefined;
2554
- slug?: string | undefined;
2555
2977
  metadata?: Record<string, unknown> | undefined;
2556
2978
  gameId?: string | null | undefined;
2557
- type?: "accessory" | "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "other" | undefined;
2979
+ type?: "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "accessory" | "other" | undefined;
2558
2980
  isPlaceable?: boolean | undefined;
2559
2981
  imageUrl?: string | null | undefined;
2560
2982
  }>;
@@ -2600,10 +3022,10 @@ declare const InsertShopListingSchema: z.ZodObject<Omit<{
2600
3022
  createdAt: z.ZodOptional<z.ZodDate>;
2601
3023
  updatedAt: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
2602
3024
  }, "id" | "createdAt" | "updatedAt">, "strip", z.ZodTypeAny, {
3025
+ isActive: boolean;
2603
3026
  itemId: string;
2604
3027
  currencyId: string;
2605
3028
  price: number;
2606
- isActive: boolean;
2607
3029
  sellBackPercentage?: number | null | undefined;
2608
3030
  stock?: number | null | undefined;
2609
3031
  availableFrom?: Date | null | undefined;
@@ -2612,37 +3034,37 @@ declare const InsertShopListingSchema: z.ZodObject<Omit<{
2612
3034
  itemId: string;
2613
3035
  currencyId: string;
2614
3036
  price: number;
3037
+ isActive?: boolean | undefined;
2615
3038
  sellBackPercentage?: number | null | undefined;
2616
3039
  stock?: number | null | undefined;
2617
- isActive?: boolean | undefined;
2618
3040
  availableFrom?: Date | null | undefined;
2619
3041
  availableUntil?: Date | null | undefined;
2620
3042
  }>;
2621
3043
  declare const UpdateShopListingSchema: z.ZodObject<{
3044
+ isActive: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
2622
3045
  itemId: z.ZodOptional<z.ZodString>;
2623
3046
  currencyId: z.ZodOptional<z.ZodString>;
2624
3047
  price: z.ZodOptional<z.ZodNumber>;
2625
3048
  sellBackPercentage: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodNumber>>>;
2626
3049
  stock: z.ZodOptional<z.ZodNullable<z.ZodOptional<z.ZodNumber>>>;
2627
- isActive: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
2628
3050
  availableFrom: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodDate>>>;
2629
3051
  availableUntil: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodDate>>>;
2630
3052
  }, "strip", z.ZodTypeAny, {
3053
+ isActive?: boolean | undefined;
2631
3054
  itemId?: string | undefined;
2632
3055
  currencyId?: string | undefined;
2633
3056
  price?: number | undefined;
2634
3057
  sellBackPercentage?: number | null | undefined;
2635
3058
  stock?: number | null | undefined;
2636
- isActive?: boolean | undefined;
2637
3059
  availableFrom?: Date | null | undefined;
2638
3060
  availableUntil?: Date | null | undefined;
2639
3061
  }, {
3062
+ isActive?: boolean | undefined;
2640
3063
  itemId?: string | undefined;
2641
3064
  currencyId?: string | undefined;
2642
3065
  price?: number | undefined;
2643
3066
  sellBackPercentage?: number | null | undefined;
2644
3067
  stock?: number | null | undefined;
2645
- isActive?: boolean | undefined;
2646
3068
  availableFrom?: Date | null | undefined;
2647
3069
  availableUntil?: Date | null | undefined;
2648
3070
  }>;
@@ -2729,31 +3151,21 @@ type PlayerCharacter = typeof playerCharacters.$inferSelect & {
2729
3151
  };
2730
3152
  type PlayerCharacterAccessory = typeof playerCharacterAccessories.$inferSelect;
2731
3153
  type GameRow = typeof games.$inferSelect;
2732
- type BaseGame = Omit<GameRow, 'gameType' | 'assetBundleBase' | 'externalUrl'>;
3154
+ type BaseGame = Omit<GameRow, 'gameType' | 'deploymentUrl' | 'externalUrl'>;
2733
3155
  type HostedGame = BaseGame & {
2734
3156
  gameType: 'hosted';
2735
- assetBundleBase: string;
3157
+ deploymentUrl: string;
2736
3158
  externalUrl: null;
2737
3159
  };
2738
3160
  type ExternalGame = BaseGame & {
2739
3161
  gameType: 'external';
2740
- assetBundleBase: null;
3162
+ deploymentUrl: null;
2741
3163
  externalUrl: string;
2742
3164
  };
2743
3165
  type Game = HostedGame | ExternalGame;
2744
3166
  type GameStateData = Record<string, unknown>;
3167
+ type GameCustomHostname = typeof gameCustomHostnames.$inferSelect;
2745
3168
  type UpsertGameMetadataInput = z.infer<typeof UpsertGameMetadataSchema>;
2746
- /**
2747
- * Response from backend deployment API
2748
- */
2749
- interface BackendDeploymentResponse {
2750
- /** Unique deployment ID */
2751
- deploymentId: string;
2752
- /** Backend API URL */
2753
- url: string;
2754
- /** Deployment timestamp */
2755
- deployedAt: string;
2756
- }
2757
3169
  type Item = typeof items.$inferSelect;
2758
3170
  type InventoryItem = typeof inventoryItems.$inferSelect;
2759
3171
  type ShopListing = typeof shopListings.$inferSelect;
@@ -2837,10 +3249,12 @@ type CharacterComponentWithSpriteUrl = CharacterComponent & {
2837
3249
  type InventoryItemWithItem = Omit<InventoryItem, 'itemId'> & {
2838
3250
  item: Item;
2839
3251
  };
2840
- type HostedGameWithManifest = HostedGame & {
2841
- manifest: ManifestV1;
3252
+ /**
3253
+ * Game with optional manifest metadata from build tools
3254
+ */
3255
+ type FetchedGame = (HostedGame | ExternalGame | GameRow) & {
3256
+ manifest?: ManifestV1;
2842
3257
  };
2843
- type FetchedGame = HostedGameWithManifest | GameRow;
2844
3258
  /**
2845
3259
  * Map-related Composite Types
2846
3260
  * Types that combine map, game, and item data for rendering and interaction
@@ -3114,38 +3528,123 @@ type EndActivityResponse = {
3114
3528
  };
3115
3529
 
3116
3530
  /**
3117
- * @fileoverview Server SDK Type Definitions
3531
+ * OAuth 2.0 implementation for the Playcademy SDK
3532
+ */
3533
+
3534
+ /**
3535
+ * Parses an OAuth state parameter to extract CSRF token and any encoded data.
3118
3536
  *
3119
- * TypeScript type definitions for the server-side Playcademy SDK.
3120
- * Includes configuration types, client state, and re-exported TimeBack types.
3537
+ * @param state - The OAuth state parameter to parse
3538
+ * @returns Object containing CSRF token and optional decoded data
3121
3539
  */
3540
+ declare function parseOAuthState(state: string): {
3541
+ csrfToken: string;
3542
+ data?: Record<string, string>;
3543
+ };
3122
3544
 
3123
3545
  /**
3124
- * TimeBack integration configuration for Playcademy config file
3546
+ * Response type for the realtime token API
3125
3547
  */
3126
- interface TimebackIntegrationConfig {
3127
- /** Organization overrides */
3128
- organization?: Partial<OrganizationConfig>;
3129
- /** Course configuration (subjects and grades REQUIRED) */
3130
- course: CourseConfig;
3131
- /** Component overrides */
3132
- component?: Partial<ComponentConfig>;
3133
- /** Resource overrides */
3134
- resource?: Partial<ResourceConfig>;
3135
- /** Component-Resource link overrides */
3136
- componentResource?: Partial<ComponentResourceConfig>;
3548
+ interface RealtimeTokenResponse {
3549
+ token: string;
3137
3550
  }
3551
+
3138
3552
  /**
3139
- * Custom API routes integration
3553
+ * Cache configuration types for runtime customization
3140
3554
  */
3141
- interface CustomRoutesIntegration {
3142
- /** Directory for custom API routes (defaults to 'server/api') */
3143
- directory?: string;
3144
- }
3145
3555
  /**
3146
- * Database integration
3556
+ * Runtime configuration for TTL cache behavior
3147
3557
  */
3148
- interface DatabaseIntegration {
3558
+ interface TTLCacheConfig {
3559
+ /** Time-to-live in milliseconds. Set to 0 to disable caching for this call. */
3560
+ ttl?: number;
3561
+ /** Force refresh, bypassing cache */
3562
+ force?: boolean;
3563
+ /** Skip cache and fetch fresh data (alias for force) */
3564
+ skipCache?: boolean;
3565
+ }
3566
+ /**
3567
+ * Runtime configuration for cooldown cache behavior
3568
+ */
3569
+ interface CooldownCacheConfig {
3570
+ /** Cooldown period in milliseconds. Set to 0 to disable cooldown for this call. */
3571
+ cooldown?: number;
3572
+ /** Force refresh, bypassing cooldown */
3573
+ force?: boolean;
3574
+ }
3575
+
3576
+ interface CharacterComponentsOptions {
3577
+ /**
3578
+ * Optional level filter for components
3579
+ * When provided, only components available at this level or below are returned
3580
+ */
3581
+ level?: number;
3582
+ /**
3583
+ * Whether to bypass the cache and force a fresh API request
3584
+ * Default: false (use cache when available)
3585
+ */
3586
+ skipCache?: boolean;
3587
+ }
3588
+ interface CreateCharacterData {
3589
+ bodyComponentId: string;
3590
+ eyesComponentId: string;
3591
+ hairstyleComponentId: string;
3592
+ outfitComponentId: string;
3593
+ }
3594
+ interface UpdateCharacterData {
3595
+ bodyComponentId?: string;
3596
+ eyesComponentId?: string;
3597
+ hairstyleComponentId?: string;
3598
+ outfitComponentId?: string;
3599
+ }
3600
+
3601
+ interface ScoreSubmission {
3602
+ id: string;
3603
+ score: number;
3604
+ achievedAt: Date;
3605
+ }
3606
+
3607
+ /**
3608
+ * Combined response type for summary method
3609
+ */
3610
+ type XpSummaryResponse = {
3611
+ today: TodayXpResponse;
3612
+ total: TotalXpResponse;
3613
+ };
3614
+
3615
+ /**
3616
+ * @fileoverview Server SDK Type Definitions
3617
+ *
3618
+ * TypeScript type definitions for the server-side Playcademy SDK.
3619
+ * Includes configuration types, client state, and re-exported TimeBack types.
3620
+ */
3621
+
3622
+ /**
3623
+ * TimeBack integration configuration for Playcademy config file
3624
+ */
3625
+ interface TimebackIntegrationConfig {
3626
+ /** Organization overrides */
3627
+ organization?: Partial<OrganizationConfig>;
3628
+ /** Course configuration (subjects and grades REQUIRED) */
3629
+ course: CourseConfig;
3630
+ /** Component overrides */
3631
+ component?: Partial<ComponentConfig>;
3632
+ /** Resource overrides */
3633
+ resource?: Partial<ResourceConfig>;
3634
+ /** Component-Resource link overrides */
3635
+ componentResource?: Partial<ComponentResourceConfig>;
3636
+ }
3637
+ /**
3638
+ * Custom API routes integration
3639
+ */
3640
+ interface CustomRoutesIntegration {
3641
+ /** Directory for custom API routes (defaults to 'server/api') */
3642
+ directory?: string;
3643
+ }
3644
+ /**
3645
+ * Database integration
3646
+ */
3647
+ interface DatabaseIntegration {
3149
3648
  /** Database directory (defaults to 'db') */
3150
3649
  directory?: string;
3151
3650
  }
@@ -3164,6 +3663,8 @@ interface IntegrationsConfig {
3164
3663
  kv?: boolean;
3165
3664
  /** Bucket storage (optional) */
3166
3665
  bucket?: boolean;
3666
+ /** Authentication (optional) */
3667
+ auth?: boolean;
3167
3668
  }
3168
3669
  /**
3169
3670
  * Unified Playcademy configuration
@@ -3218,295 +3719,6 @@ interface BackendDeploymentBundle {
3218
3719
  secrets?: Record<string, string>;
3219
3720
  }
3220
3721
 
3221
- type TokenType = 'session' | 'apiKey' | 'gameJwt';
3222
- interface ClientConfig {
3223
- baseUrl: string;
3224
- gameUrl?: string;
3225
- token?: string;
3226
- tokenType?: TokenType;
3227
- gameId?: string;
3228
- autoStartSession?: boolean;
3229
- }
3230
- type AuthProviderType = (typeof AUTH_PROVIDER_IDS)[keyof typeof AUTH_PROVIDER_IDS];
3231
- interface AuthOptions {
3232
- /** The identity provider to use for authentication */
3233
- provider: AuthProviderType;
3234
- /** The OAuth callback URL where your server handles the callback */
3235
- callbackUrl: string;
3236
- /** Authentication mode - auto detects best option based on context */
3237
- mode?: 'auto' | 'popup' | 'redirect';
3238
- /** Callback for authentication state changes */
3239
- onStateChange?: (state: AuthStateUpdate) => void;
3240
- /** Custom OAuth configuration (for users embedding the SDK outside of the Playcademy platform) */
3241
- oauth?: {
3242
- clientId: string;
3243
- authorizationEndpoint?: string;
3244
- tokenEndpoint?: string;
3245
- scope?: string;
3246
- };
3247
- /**
3248
- * Optional custom data to encode in OAuth state parameter.
3249
- * By default, the SDK automatically includes playcademy_user_id and game_id.
3250
- * Use this to add additional custom data if needed.
3251
- */
3252
- stateData?: Record<string, string>;
3253
- }
3254
- interface AuthStateUpdate {
3255
- /** Current status of the authentication flow */
3256
- status: 'opening_popup' | 'exchanging_token' | 'complete' | 'error';
3257
- /** Human-readable message about the current state */
3258
- message: string;
3259
- /** Error details if status is 'error' */
3260
- error?: Error;
3261
- }
3262
- interface AuthResult {
3263
- /** Whether authentication was successful */
3264
- success: boolean;
3265
- /** User information if authentication was successful */
3266
- user?: UserInfo;
3267
- /** Error if authentication failed */
3268
- error?: Error;
3269
- }
3270
- /**
3271
- * Authentication state change event payload.
3272
- * Used when authentication state changes in the application.
3273
- */
3274
- interface AuthStateChangePayload {
3275
- /** Whether the user is currently authenticated */
3276
- authenticated: boolean;
3277
- /** User information if authenticated, null otherwise */
3278
- user: UserInfo | null;
3279
- /** Error information if authentication failed */
3280
- error: Error | null;
3281
- }
3282
- /**
3283
- * OAuth callback event payload.
3284
- * Used when OAuth flow completes in popup/new-tab windows.
3285
- */
3286
- interface AuthCallbackPayload {
3287
- /** OAuth authorization code */
3288
- code: string;
3289
- /** OAuth state parameter for CSRF protection */
3290
- state: string;
3291
- /** Error message if OAuth flow failed */
3292
- error: string | null;
3293
- }
3294
- /**
3295
- * Token refresh event payload.
3296
- * Sent when authentication token is updated.
3297
- */
3298
- interface TokenRefreshPayload {
3299
- /** New authentication token */
3300
- token: string;
3301
- /** Token expiration timestamp */
3302
- exp: number;
3303
- }
3304
- /**
3305
- * Telemetry event payload.
3306
- * Performance metrics sent from the game.
3307
- */
3308
- interface TelemetryPayload {
3309
- /** Frames per second */
3310
- fps: number;
3311
- /** Memory usage in MB */
3312
- mem: number;
3313
- }
3314
- /**
3315
- * Keyboard event payload.
3316
- * Key events forwarded from the game.
3317
- */
3318
- interface KeyEventPayload {
3319
- /** Key value (e.g., 'Escape', 'F1') */
3320
- key: string;
3321
- /** Key code (optional) */
3322
- code?: string;
3323
- /** Event type */
3324
- type: 'keydown' | 'keyup';
3325
- }
3326
- interface ClientEvents {
3327
- authChange: {
3328
- token: string | null;
3329
- };
3330
- inventoryChange: {
3331
- itemId: string;
3332
- delta: number;
3333
- newTotal: number;
3334
- };
3335
- levelUp: {
3336
- oldLevel: number;
3337
- newLevel: number;
3338
- creditsAwarded: number;
3339
- };
3340
- xpGained: {
3341
- amount: number;
3342
- totalXP: number;
3343
- leveledUp: boolean;
3344
- };
3345
- }
3346
- type GameContextPayload = {
3347
- token: string;
3348
- baseUrl: string;
3349
- realtimeUrl: string;
3350
- gameId: string;
3351
- forwardKeys?: string[];
3352
- };
3353
- type LoginResponse = {
3354
- token: string;
3355
- };
3356
- type GameTokenResponse = {
3357
- token: string;
3358
- exp: number;
3359
- };
3360
- type StartSessionResponse = {
3361
- sessionId: string;
3362
- };
3363
- type InventoryMutationResponse = {
3364
- newTotal: number;
3365
- };
3366
- type DevUploadEvent = {
3367
- type: 'init';
3368
- } | {
3369
- type: 's3Progress';
3370
- loaded: number;
3371
- total: number;
3372
- percent: number;
3373
- } | {
3374
- type: 'finalizeStart';
3375
- } | {
3376
- type: 'finalizeProgress';
3377
- percent: number;
3378
- currentFileLabel?: string;
3379
- } | {
3380
- type: 'finalizeStatus';
3381
- message: string;
3382
- } | {
3383
- type: 'close';
3384
- };
3385
- type DevUploadHooks = {
3386
- onEvent?: (e: DevUploadEvent) => void;
3387
- onClose?: () => void;
3388
- };
3389
- /**
3390
- * Better-auth API key creation response
3391
- */
3392
- interface BetterAuthApiKeyResponse {
3393
- apiKey: string;
3394
- key: {
3395
- id: string;
3396
- name: string | null;
3397
- expiresAt: string | null;
3398
- createdAt: string;
3399
- };
3400
- }
3401
- /**
3402
- * Better-auth API key list item
3403
- */
3404
- interface BetterAuthApiKey {
3405
- id: string;
3406
- name: string | null;
3407
- start: string;
3408
- enabled: boolean;
3409
- expiresAt: string | null;
3410
- createdAt: string;
3411
- updatedAt: string;
3412
- lastRequest: string | null;
3413
- requestCount: number;
3414
- }
3415
- /**
3416
- * Bucket file metadata
3417
- */
3418
- interface BucketFile {
3419
- key: string;
3420
- size: number;
3421
- uploaded: string;
3422
- contentType?: string;
3423
- }
3424
-
3425
- /**
3426
- * OAuth 2.0 implementation for the Playcademy SDK
3427
- */
3428
-
3429
- /**
3430
- * Parses an OAuth state parameter to extract CSRF token and any encoded data.
3431
- *
3432
- * @param state - The OAuth state parameter to parse
3433
- * @returns Object containing CSRF token and optional decoded data
3434
- */
3435
- declare function parseOAuthState(state: string): {
3436
- csrfToken: string;
3437
- data?: Record<string, string>;
3438
- };
3439
-
3440
- /**
3441
- * Response type for the realtime token API
3442
- */
3443
- interface RealtimeTokenResponse {
3444
- token: string;
3445
- }
3446
-
3447
- /**
3448
- * Cache configuration types for runtime customization
3449
- */
3450
- /**
3451
- * Runtime configuration for TTL cache behavior
3452
- */
3453
- interface TTLCacheConfig {
3454
- /** Time-to-live in milliseconds. Set to 0 to disable caching for this call. */
3455
- ttl?: number;
3456
- /** Force refresh, bypassing cache */
3457
- force?: boolean;
3458
- /** Skip cache and fetch fresh data (alias for force) */
3459
- skipCache?: boolean;
3460
- }
3461
- /**
3462
- * Runtime configuration for cooldown cache behavior
3463
- */
3464
- interface CooldownCacheConfig {
3465
- /** Cooldown period in milliseconds. Set to 0 to disable cooldown for this call. */
3466
- cooldown?: number;
3467
- /** Force refresh, bypassing cooldown */
3468
- force?: boolean;
3469
- }
3470
-
3471
- interface CharacterComponentsOptions {
3472
- /**
3473
- * Optional level filter for components
3474
- * When provided, only components available at this level or below are returned
3475
- */
3476
- level?: number;
3477
- /**
3478
- * Whether to bypass the cache and force a fresh API request
3479
- * Default: false (use cache when available)
3480
- */
3481
- skipCache?: boolean;
3482
- }
3483
- interface CreateCharacterData {
3484
- bodyComponentId: string;
3485
- eyesComponentId: string;
3486
- hairstyleComponentId: string;
3487
- outfitComponentId: string;
3488
- }
3489
- interface UpdateCharacterData {
3490
- bodyComponentId?: string;
3491
- eyesComponentId?: string;
3492
- hairstyleComponentId?: string;
3493
- outfitComponentId?: string;
3494
- }
3495
-
3496
- interface ScoreSubmission {
3497
- id: string;
3498
- score: number;
3499
- achievedAt: Date;
3500
- }
3501
-
3502
- /**
3503
- * Combined response type for summary method
3504
- */
3505
- type XpSummaryResponse = {
3506
- today: TodayXpResponse;
3507
- total: TotalXpResponse;
3508
- };
3509
-
3510
3722
  interface UserScore {
3511
3723
  id: string;
3512
3724
  score: number;
@@ -3544,6 +3756,8 @@ interface UserScore {
3544
3756
  declare function init(options?: {
3545
3757
  baseUrl?: string;
3546
3758
  allowedParentOrigins?: string[];
3759
+ onDisconnect?: DisconnectHandler;
3760
+ enableConnectionMonitoring?: boolean;
3547
3761
  }): Promise<PlaycademyClient>;
3548
3762
 
3549
3763
  /**
@@ -3597,6 +3811,7 @@ declare class PlaycademyClient {
3597
3811
  private internalClientSessionId?;
3598
3812
  private authContext?;
3599
3813
  private initPayload?;
3814
+ private connectionManager?;
3600
3815
  /**
3601
3816
  * Creates a new PlaycademyClient instance.
3602
3817
  *
@@ -3643,51 +3858,130 @@ declare class PlaycademyClient {
3643
3858
  *
3644
3859
  * @returns The token type
3645
3860
  */
3646
- getTokenType(): TokenType;
3861
+ getTokenType(): TokenType;
3862
+ /**
3863
+ * Gets the current authentication token.
3864
+ *
3865
+ * @returns The current token or null if not authenticated
3866
+ *
3867
+ * @example
3868
+ * ```typescript
3869
+ * // Send token to your backend for verification
3870
+ * const token = client.getToken()
3871
+ * const response = await fetch('/api/auth/playcademy', {
3872
+ * method: 'POST',
3873
+ * body: JSON.stringify({ gameToken: token })
3874
+ * })
3875
+ * ```
3876
+ */
3877
+ getToken(): string | null;
3878
+ /**
3879
+ * Checks if the client has a valid API token for making Playcademy API requests.
3880
+ *
3881
+ * For games (iframe context): Checks if we have a valid token from the parent.
3882
+ * For Cademy (standalone): Checks if we have a token from better-auth.
3883
+ *
3884
+ * Note: This checks for API authentication, not whether a user has linked
3885
+ * their identity via OAuth.
3886
+ *
3887
+ * @returns true if API token exists, false otherwise
3888
+ *
3889
+ * @example
3890
+ * ```typescript
3891
+ * if (client.isAuthenticated()) {
3892
+ * // Can make API calls
3893
+ * const games = await client.games.list()
3894
+ * } else {
3895
+ * console.error('No API token available')
3896
+ * }
3897
+ * ```
3898
+ */
3899
+ isAuthenticated(): boolean;
3900
+ /**
3901
+ * Registers a callback to be called when authentication state changes.
3902
+ *
3903
+ * @param callback - Function to call when auth state changes
3904
+ */
3905
+ onAuthChange(callback: (token: string | null) => void): void;
3906
+ /**
3907
+ * Registers a callback to be called when connection issues are detected.
3908
+ *
3909
+ * This is a convenience method that filters connection change events to only
3910
+ * fire when the connection degrades (offline or degraded states). Use this
3911
+ * when you want to handle disconnects without being notified of recoveries.
3912
+ *
3913
+ * For all connection state changes, use `client.on('connectionChange', ...)` instead.
3914
+ *
3915
+ * @param callback - Function to call when connection state changes to offline or degraded
3916
+ * @returns Cleanup function to unregister the callback
3917
+ *
3918
+ * @example
3919
+ * ```typescript
3920
+ * const cleanup = client.onDisconnect(({ state, reason, displayAlert }) => {
3921
+ * console.log(`Connection ${state}: ${reason}`)
3922
+ *
3923
+ * if (state === 'offline') {
3924
+ * // Save state and return to game lobby
3925
+ * displayAlert?.('Connection lost. Your progress has been saved.', { type: 'error' })
3926
+ * saveGameState()
3927
+ * returnToLobby()
3928
+ * } else if (state === 'degraded') {
3929
+ * displayAlert?.('Slow connection detected.', { type: 'warning' })
3930
+ * }
3931
+ * })
3932
+ *
3933
+ * // Later: cleanup() to unregister
3934
+ * ```
3935
+ *
3936
+ * @see {@link DISCONNECT_HANDLING.md} for detailed usage examples
3937
+ * @see {@link ConnectionManager.onDisconnect} for the underlying implementation
3938
+ */
3939
+ onDisconnect(callback: (context: DisconnectContext) => void | Promise<void>): () => void;
3647
3940
  /**
3648
- * Gets the current authentication token.
3941
+ * Gets the current connection state.
3649
3942
  *
3650
- * @returns The current token or null if not authenticated
3943
+ * Returns the last known connection state without triggering a new check.
3944
+ * Use `checkConnection()` to force an immediate verification.
3945
+ *
3946
+ * @returns Current connection state ('online', 'offline', 'degraded') or 'unknown' if monitoring is disabled
3651
3947
  *
3652
3948
  * @example
3653
3949
  * ```typescript
3654
- * // Send token to your backend for verification
3655
- * const token = client.getToken()
3656
- * const response = await fetch('/api/auth/playcademy', {
3657
- * method: 'POST',
3658
- * body: JSON.stringify({ gameToken: token })
3659
- * })
3950
+ * const state = client.getConnectionState()
3951
+ * if (state === 'offline') {
3952
+ * console.log('No connection available')
3953
+ * }
3660
3954
  * ```
3955
+ *
3956
+ * @see {@link checkConnection} to trigger an immediate connection check
3957
+ * @see {@link ConnectionManager.getState} for the underlying implementation
3661
3958
  */
3662
- getToken(): string | null;
3959
+ getConnectionState(): ConnectionState | 'unknown';
3663
3960
  /**
3664
- * Checks if the client has a valid API token for making Playcademy API requests.
3665
- *
3666
- * For games (iframe context): Checks if we have a valid token from the parent.
3667
- * For Cademy (standalone): Checks if we have a token from better-auth.
3961
+ * Manually triggers a connection check immediately.
3668
3962
  *
3669
- * Note: This checks for API authentication, not whether a user has linked
3670
- * their identity via OAuth.
3963
+ * Forces a heartbeat ping to verify connectivity right now, bypassing the normal
3964
+ * interval. Useful when you need to verify connection status before a critical
3965
+ * operation (e.g., saving important game state).
3671
3966
  *
3672
- * @returns true if API token exists, false otherwise
3967
+ * @returns Promise resolving to the current connection state after verification
3673
3968
  *
3674
3969
  * @example
3675
3970
  * ```typescript
3676
- * if (client.isAuthenticated()) {
3677
- * // Can make API calls
3678
- * const games = await client.games.list()
3679
- * } else {
3680
- * console.error('No API token available')
3971
+ * // Check before critical operation
3972
+ * const state = await client.checkConnection()
3973
+ * if (state !== 'online') {
3974
+ * alert('Please check your internet connection before saving')
3975
+ * return
3681
3976
  * }
3977
+ *
3978
+ * await client.games.saveState(importantData)
3682
3979
  * ```
3683
- */
3684
- isAuthenticated(): boolean;
3685
- /**
3686
- * Registers a callback to be called when authentication state changes.
3687
3980
  *
3688
- * @param callback - Function to call when auth state changes
3981
+ * @see {@link getConnectionState} to get the last known state without checking
3982
+ * @see {@link ConnectionManager.checkNow} for the underlying implementation
3689
3983
  */
3690
- onAuthChange(callback: (token: string | null) => void): void;
3984
+ checkConnection(): Promise<ConnectionState | 'unknown'>;
3691
3985
  /**
3692
3986
  * Sets the authentication context for the client.
3693
3987
  * This is called during initialization to store environment info.
@@ -3750,6 +4044,11 @@ declare class PlaycademyClient {
3750
4044
  * Safe to call in any environment - isInIframe handles browser detection.
3751
4045
  */
3752
4046
  private _detectAuthContext;
4047
+ /**
4048
+ * Initializes connection monitoring if enabled.
4049
+ * Safe to call in any environment - only runs in browser.
4050
+ */
4051
+ private _initializeConnectionMonitor;
3753
4052
  /**
3754
4053
  * Initializes an internal game session for automatic session management.
3755
4054
  * Only starts a session if:
@@ -3873,15 +4172,17 @@ declare class PlaycademyClient {
3873
4172
  get: () => Promise<DeveloperStatusValue>;
3874
4173
  };
3875
4174
  games: {
3876
- deploy: {
3877
- frontend: (slug: string, metadata: UpsertGameMetadataInput, file: File | Blob | null, hooks?: DevUploadHooks) => Promise<Game>;
3878
- backend: (slug: string, bundle: BackendDeploymentBundle) => Promise<BackendDeploymentResponse>;
3879
- seed: (slug: string, code: string, environment?: "staging" | "production") => Promise<{
3880
- success: boolean;
3881
- deploymentId: string;
3882
- executedAt: string;
3883
- }>;
3884
- };
4175
+ deploy: (slug: string, options: {
4176
+ metadata?: UpsertGameMetadataInput;
4177
+ file?: File | Blob | null;
4178
+ backend?: BackendDeploymentBundle;
4179
+ hooks?: DevUploadHooks;
4180
+ }) => Promise<Game>;
4181
+ seed: (slug: string, code: string, environment?: "staging" | "production") => Promise<{
4182
+ success: boolean;
4183
+ deploymentId: string;
4184
+ executedAt: string;
4185
+ }>;
3885
4186
  upsert: (slug: string, metadata: UpsertGameMetadataInput) => Promise<Game>;
3886
4187
  delete: (gameId: string) => Promise<void>;
3887
4188
  secrets: {
@@ -3907,6 +4208,12 @@ declare class PlaycademyClient {
3907
4208
  put: (slug: string, key: string, content: Blob | ArrayBuffer | Uint8Array, contentType?: string) => Promise<void>;
3908
4209
  delete: (slug: string, key: string) => Promise<void>;
3909
4210
  };
4211
+ domains: {
4212
+ add: (slug: string, hostname: string) => Promise<GameCustomHostname>;
4213
+ list: (slug: string) => Promise<GameCustomHostname[]>;
4214
+ status: (slug: string, hostname: string, refresh?: boolean) => Promise<GameCustomHostname>;
4215
+ delete: (slug: string, hostname: string) => Promise<void>;
4216
+ };
3910
4217
  };
3911
4218
  items: {
3912
4219
  create: (gameId: string, slug: string, itemData: Omit<InsertItemInput, "slug" | "gameId">) => Promise<Item>;
@@ -3943,50 +4250,50 @@ declare class PlaycademyClient {
3943
4250
  };
3944
4251
  items: {
3945
4252
  create: (props: InsertItemInput) => Promise<{
3946
- metadata: unknown;
3947
- type: "accessory" | "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "other";
3948
- slug: string;
3949
4253
  id: string;
3950
4254
  createdAt: Date;
3951
4255
  gameId: string | null;
4256
+ slug: string;
3952
4257
  displayName: string;
4258
+ metadata: unknown;
3953
4259
  description: string | null;
4260
+ type: "accessory" | "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "other";
3954
4261
  isPlaceable: boolean;
3955
4262
  imageUrl: string | null;
3956
4263
  }>;
3957
4264
  get: (itemId: string) => Promise<{
3958
- metadata: unknown;
3959
- type: "accessory" | "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "other";
3960
- slug: string;
3961
4265
  id: string;
3962
4266
  createdAt: Date;
3963
4267
  gameId: string | null;
4268
+ slug: string;
3964
4269
  displayName: string;
4270
+ metadata: unknown;
3965
4271
  description: string | null;
4272
+ type: "accessory" | "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "other";
3966
4273
  isPlaceable: boolean;
3967
4274
  imageUrl: string | null;
3968
4275
  }>;
3969
4276
  list: () => Promise<{
3970
- metadata: unknown;
3971
- type: "accessory" | "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "other";
3972
- slug: string;
3973
4277
  id: string;
3974
4278
  createdAt: Date;
3975
4279
  gameId: string | null;
4280
+ slug: string;
3976
4281
  displayName: string;
4282
+ metadata: unknown;
3977
4283
  description: string | null;
4284
+ type: "accessory" | "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "other";
3978
4285
  isPlaceable: boolean;
3979
4286
  imageUrl: string | null;
3980
4287
  }[]>;
3981
4288
  update: (itemId: string, props: UpdateItemInput) => Promise<{
3982
- metadata: unknown;
3983
- type: "accessory" | "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "other";
3984
- slug: string;
3985
4289
  id: string;
3986
4290
  createdAt: Date;
3987
4291
  gameId: string | null;
4292
+ slug: string;
3988
4293
  displayName: string;
4294
+ metadata: unknown;
3989
4295
  description: string | null;
4296
+ type: "accessory" | "currency" | "badge" | "trophy" | "collectible" | "consumable" | "unlock" | "upgrade" | "other";
3990
4297
  isPlaceable: boolean;
3991
4298
  imageUrl: string | null;
3992
4299
  }>;
@@ -4121,9 +4428,7 @@ declare class PlaycademyClient {
4121
4428
  }) => Promise<TodayXpResponse>;
4122
4429
  total: () => Promise<TotalXpResponse>;
4123
4430
  history: (options?: {
4124
- startDate
4125
- /** Auto-initializes a PlaycademyClient with context from the environment */
4126
- ?: string;
4431
+ startDate?: string;
4127
4432
  endDate?: string;
4128
4433
  }) => Promise<XpHistoryResponse>;
4129
4434
  summary: (options?: {
@@ -4154,86 +4459,517 @@ declare class PlaycademyClient {
4154
4459
  limit?: number;
4155
4460
  }) => Promise<UserScore$1[]>;
4156
4461
  };
4157
- /** Character methods (get, create, update, components) */
4158
- character: {
4159
- get: (userId?: string) => Promise<PlayerCharacter | null>;
4160
- create: (characterData: CreateCharacterData) => Promise<PlayerCharacter>;
4161
- update: (updates: UpdateCharacterData) => Promise<PlayerCharacter>;
4162
- components: {
4163
- list: (options?: CharacterComponentsOptions & TTLCacheConfig) => Promise<CharacterComponentWithSpriteUrl[]>;
4164
- clearCache: (key?: string) => void;
4165
- getCacheKeys: () => string[];
4166
- };
4167
- accessories: {
4168
- equip: (slot: string, componentId: string) => Promise<PlayerCharacterAccessory>;
4169
- remove: (slot: string) => Promise<{
4170
- success: boolean;
4171
- }>;
4172
- list: () => Promise<PlayerCharacterAccessory[]>;
4173
- };
4462
+ /** Character methods (get, create, update, components) */
4463
+ character: {
4464
+ get: (userId?: string) => Promise<PlayerCharacter | null>;
4465
+ create: (characterData: CreateCharacterData) => Promise<PlayerCharacter>;
4466
+ update: (updates: UpdateCharacterData) => Promise<PlayerCharacter>;
4467
+ components: {
4468
+ list: (options?: CharacterComponentsOptions & TTLCacheConfig) => Promise<CharacterComponentWithSpriteUrl[]>;
4469
+ clearCache: (key?: string) => void;
4470
+ getCacheKeys: () => string[];
4471
+ };
4472
+ accessories: {
4473
+ equip: (slot: string, componentId: string) => Promise<PlayerCharacterAccessory>;
4474
+ remove: (slot: string) => Promise<{
4475
+ success: boolean;
4476
+ }>;
4477
+ list: () => Promise<PlayerCharacterAccessory[]>;
4478
+ };
4479
+ };
4480
+ /** Sprites methods (templates) */
4481
+ sprites: {
4482
+ templates: {
4483
+ get: (slug: string) => Promise<SpriteTemplateData>;
4484
+ };
4485
+ };
4486
+ /** Realtime methods (token) */
4487
+ realtime: {
4488
+ token: {
4489
+ get: () => Promise<RealtimeTokenResponse>;
4490
+ };
4491
+ open(channel?: string, url?: string): Promise<_playcademy_realtime_server_types.RealtimeChannel>;
4492
+ };
4493
+ /** Achievements methods (list, history, progress) */
4494
+ achievements: {
4495
+ list: (options?: TTLCacheConfig) => Promise<AchievementCurrent[]>;
4496
+ history: {
4497
+ list: (queryOptions?: {
4498
+ limit?: number;
4499
+ }, cacheOptions?: TTLCacheConfig) => Promise<AchievementHistoryEntry[]>;
4500
+ };
4501
+ progress: {
4502
+ submit: (achievementId: string) => Promise<AchievementProgressResponse>;
4503
+ };
4504
+ };
4505
+ /** Notifications methods (list, update status, stats) */
4506
+ notifications: {
4507
+ list: (queryOptions?: {
4508
+ status?: NotificationStatus;
4509
+ type?: NotificationType;
4510
+ limit?: number;
4511
+ offset?: number;
4512
+ }, cacheOptions?: TTLCacheConfig) => Promise<Notification[]>;
4513
+ markAsSeen: (notificationId: string) => Promise<Notification>;
4514
+ markAsClicked: (notificationId: string) => Promise<Notification>;
4515
+ dismiss: (notificationId: string) => Promise<Notification>;
4516
+ stats: {
4517
+ get: (queryOptions?: {
4518
+ from?: string;
4519
+ to?: string;
4520
+ }, cacheOptions?: TTLCacheConfig) => Promise<NotificationStats>;
4521
+ };
4522
+ };
4523
+ /** Backend methods for calling custom game API routes */
4524
+ backend: {
4525
+ get<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
4526
+ post<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
4527
+ put<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
4528
+ patch<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
4529
+ delete<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
4530
+ request<T = unknown>(path: string, method: Method, body?: unknown, headers?: Record<string, string>): Promise<T>;
4531
+ download(path: string, method?: Method, body?: unknown, headers?: Record<string, string>): Promise<Response>;
4532
+ url(pathOrStrings: string | TemplateStringsArray, ...values: unknown[]): string;
4533
+ };
4534
+ /** Auto-initializes a PlaycademyClient with context from the environment */
4535
+ static init: typeof init;
4536
+ /** Authenticates a user with email and password */
4537
+ static login: typeof login;
4538
+ /** Static identity utilities for OAuth operations */
4539
+ static identity: {
4540
+ parseOAuthState: typeof parseOAuthState;
4541
+ };
4542
+ }
4543
+
4544
+ type TokenType = 'session' | 'apiKey' | 'gameJwt';
4545
+ interface ClientConfig {
4546
+ baseUrl: string;
4547
+ gameUrl?: string;
4548
+ token?: string;
4549
+ tokenType?: TokenType;
4550
+ gameId?: string;
4551
+ autoStartSession?: boolean;
4552
+ onDisconnect?: DisconnectHandler;
4553
+ enableConnectionMonitoring?: boolean;
4554
+ }
4555
+ /**
4556
+ * Handler called when connection state changes to offline or degraded.
4557
+ * Games can implement this to handle disconnects gracefully.
4558
+ */
4559
+ type DisconnectHandler = (context: DisconnectContext) => void | Promise<void>;
4560
+ /**
4561
+ * Context provided to disconnect handlers
4562
+ */
4563
+ interface DisconnectContext {
4564
+ /** Current connection state */
4565
+ state: 'offline' | 'degraded';
4566
+ /** Reason for the disconnect */
4567
+ reason: string;
4568
+ /** Timestamp when disconnect was detected */
4569
+ timestamp: number;
4570
+ /** Utility to display a platform-level alert */
4571
+ displayAlert: (message: string, options?: {
4572
+ type?: 'info' | 'warning' | 'error';
4573
+ duration?: number;
4574
+ }) => void;
4575
+ }
4576
+ type AuthProviderType = (typeof AUTH_PROVIDER_IDS)[keyof typeof AUTH_PROVIDER_IDS];
4577
+ interface AuthOptions {
4578
+ /** The identity provider to use for authentication */
4579
+ provider: AuthProviderType;
4580
+ /** The OAuth callback URL where your server handles the callback */
4581
+ callbackUrl: string;
4582
+ /** Authentication mode - auto detects best option based on context */
4583
+ mode?: 'auto' | 'popup' | 'redirect';
4584
+ /** Callback for authentication state changes */
4585
+ onStateChange?: (state: AuthStateUpdate) => void;
4586
+ /** Custom OAuth configuration (for users embedding the SDK outside of the Playcademy platform) */
4587
+ oauth?: {
4588
+ clientId: string;
4589
+ authorizationEndpoint?: string;
4590
+ tokenEndpoint?: string;
4591
+ scope?: string;
4592
+ };
4593
+ /**
4594
+ * Optional custom data to encode in OAuth state parameter.
4595
+ * By default, the SDK automatically includes playcademy_user_id and game_id.
4596
+ * Use this to add additional custom data if needed.
4597
+ */
4598
+ stateData?: Record<string, string>;
4599
+ }
4600
+ interface AuthStateUpdate {
4601
+ /** Current status of the authentication flow */
4602
+ status: 'opening_popup' | 'exchanging_token' | 'complete' | 'error';
4603
+ /** Human-readable message about the current state */
4604
+ message: string;
4605
+ /** Error details if status is 'error' */
4606
+ error?: Error;
4607
+ }
4608
+ interface AuthResult {
4609
+ /** Whether authentication was successful */
4610
+ success: boolean;
4611
+ /** User information if authentication was successful */
4612
+ user?: UserInfo;
4613
+ /** Error if authentication failed */
4614
+ error?: Error;
4615
+ }
4616
+ /**
4617
+ * Authentication state change event payload.
4618
+ * Used when authentication state changes in the application.
4619
+ */
4620
+ interface AuthStateChangePayload {
4621
+ /** Whether the user is currently authenticated */
4622
+ authenticated: boolean;
4623
+ /** User information if authenticated, null otherwise */
4624
+ user: UserInfo | null;
4625
+ /** Error information if authentication failed */
4626
+ error: Error | null;
4627
+ }
4628
+ /**
4629
+ * OAuth callback event payload.
4630
+ * Used when OAuth flow completes in popup/new-tab windows.
4631
+ */
4632
+ interface AuthCallbackPayload {
4633
+ /** OAuth authorization code */
4634
+ code: string;
4635
+ /** OAuth state parameter for CSRF protection */
4636
+ state: string;
4637
+ /** Error message if OAuth flow failed */
4638
+ error: string | null;
4639
+ }
4640
+ /**
4641
+ * Token refresh event payload.
4642
+ * Sent when authentication token is updated.
4643
+ */
4644
+ interface TokenRefreshPayload {
4645
+ /** New authentication token */
4646
+ token: string;
4647
+ /** Token expiration timestamp */
4648
+ exp: number;
4649
+ }
4650
+ /**
4651
+ * Telemetry event payload.
4652
+ * Performance metrics sent from the game.
4653
+ */
4654
+ interface TelemetryPayload {
4655
+ /** Frames per second */
4656
+ fps: number;
4657
+ /** Memory usage in MB */
4658
+ mem: number;
4659
+ }
4660
+ /**
4661
+ * Keyboard event payload.
4662
+ * Key events forwarded from the game.
4663
+ */
4664
+ interface KeyEventPayload {
4665
+ /** Key value (e.g., 'Escape', 'F1') */
4666
+ key: string;
4667
+ /** Key code (optional) */
4668
+ code?: string;
4669
+ /** Event type */
4670
+ type: 'keydown' | 'keyup';
4671
+ }
4672
+ interface DisplayAlertPayload {
4673
+ message: string;
4674
+ options?: {
4675
+ type?: 'info' | 'warning' | 'error';
4676
+ duration?: number;
4677
+ };
4678
+ }
4679
+ /**
4680
+ * Display alert payload.
4681
+ * Request from game to show platform-level alert.
4682
+ */
4683
+ interface DisplayAlertPayload {
4684
+ message: string;
4685
+ options?: {
4686
+ type?: 'info' | 'warning' | 'error';
4687
+ duration?: number;
4174
4688
  };
4175
- /** Sprites methods (templates) */
4176
- sprites: {
4177
- templates: {
4178
- get: (slug: string) => Promise<SpriteTemplateData>;
4179
- };
4689
+ }
4690
+ /**
4691
+ * Connection state payload.
4692
+ * Broadcast from platform to games when connection changes.
4693
+ */
4694
+ interface ConnectionStatePayload {
4695
+ state: 'online' | 'offline' | 'degraded';
4696
+ reason: string;
4697
+ }
4698
+ interface ClientEvents {
4699
+ authChange: {
4700
+ token: string | null;
4180
4701
  };
4181
- /** Realtime methods (token) */
4182
- realtime: {
4183
- token: {
4184
- get: () => Promise<RealtimeTokenResponse>;
4185
- };
4186
- open(channel?: string, url?: string): Promise<_playcademy_realtime_server_types.RealtimeChannel>;
4702
+ inventoryChange: {
4703
+ itemId: string;
4704
+ delta: number;
4705
+ newTotal: number;
4187
4706
  };
4188
- /** Achievements methods (list, history, progress) */
4189
- achievements: {
4190
- list: (options?: TTLCacheConfig) => Promise<AchievementCurrent[]>;
4191
- history: {
4192
- list: (queryOptions?: {
4193
- limit?: number;
4194
- }, cacheOptions?: TTLCacheConfig) => Promise<AchievementHistoryEntry[]>;
4195
- };
4196
- progress: {
4197
- submit: (achievementId: string) => Promise<AchievementProgressResponse>;
4198
- };
4707
+ levelUp: {
4708
+ oldLevel: number;
4709
+ newLevel: number;
4710
+ creditsAwarded: number;
4199
4711
  };
4200
- /** Notifications methods (list, update status, stats) */
4201
- notifications: {
4202
- list: (queryOptions?: {
4203
- status?: NotificationStatus;
4204
- type?: NotificationType;
4205
- limit?: number;
4206
- offset?: number;
4207
- }, cacheOptions?: TTLCacheConfig) => Promise<Notification[]>;
4208
- markAsSeen: (notificationId: string) => Promise<Notification>;
4209
- markAsClicked: (notificationId: string) => Promise<Notification>;
4210
- dismiss: (notificationId: string) => Promise<Notification>;
4211
- stats: {
4212
- get: (queryOptions?: {
4213
- from?: string;
4214
- to?: string;
4215
- }, cacheOptions?: TTLCacheConfig) => Promise<NotificationStats>;
4216
- };
4712
+ xpGained: {
4713
+ amount: number;
4714
+ totalXP: number;
4715
+ leveledUp: boolean;
4217
4716
  };
4218
- /** Backend methods for calling custom game API routes */
4219
- backend: {
4220
- get<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
4221
- post<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
4222
- put<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
4223
- patch<T = unknown>(path: string, body?: unknown, headers?: Record<string, string>): Promise<T>;
4224
- delete<T = unknown>(path: string, headers?: Record<string, string>): Promise<T>;
4225
- request<T = unknown>(path: string, method: Method, body?: unknown, headers?: Record<string, string>): Promise<T>;
4226
- download(path: string, method?: Method, body?: unknown, headers?: Record<string, string>): Promise<Response>;
4227
- url(pathOrStrings: string | TemplateStringsArray, ...values: unknown[]): string;
4717
+ connectionChange: {
4718
+ state: 'online' | 'offline' | 'degraded';
4719
+ reason: string;
4228
4720
  };
4229
- /** Auto-initializes a PlaycademyClient with context from the environment */
4230
- static init: typeof init;
4231
- /** Authenticates a user with email and password */
4232
- static login: typeof login;
4233
- /** Static identity utilities for OAuth operations */
4234
- static identity: {
4235
- parseOAuthState: typeof parseOAuthState;
4721
+ }
4722
+ type GameContextPayload = {
4723
+ token: string;
4724
+ baseUrl: string;
4725
+ realtimeUrl: string;
4726
+ gameId: string;
4727
+ forwardKeys?: string[];
4728
+ };
4729
+ type LoginResponse = {
4730
+ token: string;
4731
+ };
4732
+ type GameTokenResponse = {
4733
+ token: string;
4734
+ exp: number;
4735
+ };
4736
+ type StartSessionResponse = {
4737
+ sessionId: string;
4738
+ };
4739
+ type InventoryMutationResponse = {
4740
+ newTotal: number;
4741
+ };
4742
+ type DevUploadEvent = {
4743
+ type: 'init';
4744
+ } | {
4745
+ type: 's3Progress';
4746
+ loaded: number;
4747
+ total: number;
4748
+ percent: number;
4749
+ } | {
4750
+ type: 'finalizeStart';
4751
+ } | {
4752
+ type: 'finalizeProgress';
4753
+ percent: number;
4754
+ currentFileLabel?: string;
4755
+ } | {
4756
+ type: 'finalizeStatus';
4757
+ message: string;
4758
+ } | {
4759
+ type: 'close';
4760
+ };
4761
+ type DevUploadHooks = {
4762
+ onEvent?: (e: DevUploadEvent) => void;
4763
+ onClose?: () => void;
4764
+ };
4765
+ /**
4766
+ * Better-auth API key creation response
4767
+ */
4768
+ interface BetterAuthApiKeyResponse {
4769
+ apiKey: string;
4770
+ key: {
4771
+ id: string;
4772
+ name: string | null;
4773
+ expiresAt: string | null;
4774
+ createdAt: string;
4775
+ };
4776
+ }
4777
+ /**
4778
+ * Better-auth API key list item
4779
+ */
4780
+ interface BetterAuthApiKey {
4781
+ id: string;
4782
+ name: string | null;
4783
+ start: string;
4784
+ enabled: boolean;
4785
+ expiresAt: string | null;
4786
+ createdAt: string;
4787
+ updatedAt: string;
4788
+ lastRequest: string | null;
4789
+ requestCount: number;
4790
+ }
4791
+ /**
4792
+ * Bucket file metadata
4793
+ */
4794
+ interface BucketFile {
4795
+ key: string;
4796
+ size: number;
4797
+ uploaded: string;
4798
+ contentType?: string;
4799
+ }
4800
+
4801
+ /**
4802
+ * Connection Manager
4803
+ *
4804
+ * Manages connection monitoring and integrates it with the Playcademy client.
4805
+ * Handles event wiring, state management, and disconnect callbacks.
4806
+ *
4807
+ * In iframe mode, disables local monitoring and listens to platform connection
4808
+ * state broadcasts instead (avoids duplicate heartbeats).
4809
+ */
4810
+
4811
+ /**
4812
+ * Configuration for the ConnectionManager.
4813
+ */
4814
+ interface ConnectionManagerConfig {
4815
+ /** Base URL for API requests (used for heartbeat pings) */
4816
+ baseUrl: string;
4817
+ /** Authentication context (iframe vs standalone) for alert routing */
4818
+ authContext?: {
4819
+ isInIframe: boolean;
4236
4820
  };
4821
+ /** Handler to call when connection issues are detected */
4822
+ onDisconnect?: DisconnectHandler;
4823
+ /** Callback to emit connection change events to the client */
4824
+ onConnectionChange?: (state: ConnectionState, reason: string) => void;
4825
+ }
4826
+ /**
4827
+ * Manages connection monitoring for the Playcademy client.
4828
+ *
4829
+ * The ConnectionManager serves as an integration layer between the low-level
4830
+ * ConnectionMonitor and the PlaycademyClient. It handles:
4831
+ * - Event wiring and coordination
4832
+ * - Disconnect callbacks with context
4833
+ * - Platform-level alert integration
4834
+ * - Request success/failure tracking
4835
+ *
4836
+ * This class is used internally by PlaycademyClient and typically not
4837
+ * instantiated directly by game developers.
4838
+ *
4839
+ * @see {@link ConnectionMonitor} for the underlying monitoring implementation
4840
+ * @see {@link PlaycademyClient.onDisconnect} for the public API
4841
+ */
4842
+ declare class ConnectionManager {
4843
+ private monitor?;
4844
+ private authContext?;
4845
+ private disconnectHandler?;
4846
+ private connectionChangeCallback?;
4847
+ private currentState;
4848
+ private additionalDisconnectHandlers;
4849
+ /**
4850
+ * Creates a new ConnectionManager instance.
4851
+ *
4852
+ * @param config - Configuration options for the manager
4853
+ *
4854
+ * @example
4855
+ * ```typescript
4856
+ * const manager = new ConnectionManager({
4857
+ * baseUrl: 'https://api.playcademy.com',
4858
+ * authContext: { isInIframe: false },
4859
+ * onDisconnect: (context) => {
4860
+ * console.log(`Disconnected: ${context.state}`)
4861
+ * },
4862
+ * onConnectionChange: (state, reason) => {
4863
+ * console.log(`Connection changed: ${state}`)
4864
+ * }
4865
+ * })
4866
+ * ```
4867
+ */
4868
+ constructor(config: ConnectionManagerConfig);
4869
+ /**
4870
+ * Gets the current connection state.
4871
+ *
4872
+ * @returns The current connection state ('online', 'offline', or 'degraded')
4873
+ *
4874
+ * @example
4875
+ * ```typescript
4876
+ * const state = manager.getState()
4877
+ * if (state === 'offline') {
4878
+ * console.log('No connection')
4879
+ * }
4880
+ * ```
4881
+ */
4882
+ getState(): ConnectionState;
4883
+ /**
4884
+ * Manually triggers a connection check immediately.
4885
+ *
4886
+ * Forces a heartbeat ping to verify the current connection status.
4887
+ * Useful when you need to check connectivity before a critical operation.
4888
+ *
4889
+ * In iframe mode, this returns the last known state from platform.
4890
+ *
4891
+ * @returns Promise resolving to the current connection state
4892
+ *
4893
+ * @example
4894
+ * ```typescript
4895
+ * const state = await manager.checkNow()
4896
+ * if (state === 'online') {
4897
+ * await performCriticalOperation()
4898
+ * }
4899
+ * ```
4900
+ */
4901
+ checkNow(): Promise<ConnectionState>;
4902
+ /**
4903
+ * Reports a successful API request to the connection monitor.
4904
+ *
4905
+ * This resets the consecutive failure counter and transitions from
4906
+ * 'degraded' to 'online' state if applicable.
4907
+ *
4908
+ * Typically called automatically by the SDK's request wrapper.
4909
+ * No-op in iframe mode (platform handles monitoring).
4910
+ */
4911
+ reportRequestSuccess(): void;
4912
+ /**
4913
+ * Reports a failed API request to the connection monitor.
4914
+ *
4915
+ * Only network errors are tracked (not 4xx/5xx HTTP responses).
4916
+ * After consecutive failures exceed the threshold, the state transitions
4917
+ * to 'degraded' or 'offline'.
4918
+ *
4919
+ * Typically called automatically by the SDK's request wrapper.
4920
+ * No-op in iframe mode (platform handles monitoring).
4921
+ *
4922
+ * @param error - The error from the failed request
4923
+ */
4924
+ reportRequestFailure(error: unknown): void;
4925
+ /**
4926
+ * Registers a callback to be called when connection issues are detected.
4927
+ *
4928
+ * The callback only fires for 'offline' and 'degraded' states, not when
4929
+ * recovering to 'online'. This provides a clean API for games to handle
4930
+ * disconnect scenarios without being notified of every state change.
4931
+ *
4932
+ * Works in both iframe and standalone modes transparently.
4933
+ *
4934
+ * @param callback - Function to call when connection degrades
4935
+ * @returns Cleanup function to unregister the callback
4936
+ *
4937
+ * @example
4938
+ * ```typescript
4939
+ * const cleanup = manager.onDisconnect(({ state, reason, displayAlert }) => {
4940
+ * if (state === 'offline') {
4941
+ * displayAlert?.('Connection lost. Saving your progress...', { type: 'error' })
4942
+ * saveGameState()
4943
+ * }
4944
+ * })
4945
+ *
4946
+ * // Later: cleanup() to unregister
4947
+ * ```
4948
+ */
4949
+ onDisconnect(callback: DisconnectHandler): () => void;
4950
+ /**
4951
+ * Stops connection monitoring and performs cleanup.
4952
+ *
4953
+ * Removes event listeners and clears heartbeat intervals.
4954
+ * Should be called when the client is being destroyed.
4955
+ */
4956
+ stop(): void;
4957
+ /**
4958
+ * Sets up listener for platform connection state broadcasts (iframe mode only).
4959
+ */
4960
+ private _setupPlatformListener;
4961
+ /**
4962
+ * Handles connection state changes from the monitor or platform.
4963
+ *
4964
+ * Coordinates between:
4965
+ * 1. Emitting events to the client (for client.on('connectionChange'))
4966
+ * 2. Calling the disconnect handler if configured
4967
+ * 3. Calling any additional handlers registered via onDisconnect()
4968
+ *
4969
+ * @param state - The new connection state
4970
+ * @param reason - Human-readable reason for the state change
4971
+ */
4972
+ private _handleConnectionChange;
4237
4973
  }
4238
4974
 
4239
4975
  /**
@@ -4317,6 +5053,14 @@ declare enum MessageEvents {
4317
5053
  * Payload: boolean (true = show overlay, false = hide overlay)
4318
5054
  */
4319
5055
  OVERLAY = "PLAYCADEMY_OVERLAY",
5056
+ /**
5057
+ * Broadcasts connection state changes to games.
5058
+ * Sent by platform when network connectivity changes.
5059
+ * Payload:
5060
+ * - `state`: 'online' | 'offline' | 'degraded'
5061
+ * - `reason`: string
5062
+ */
5063
+ CONNECTION_STATE = "PLAYCADEMY_CONNECTION_STATE",
4320
5064
  /**
4321
5065
  * Game has finished loading and is ready to receive messages.
4322
5066
  * Sent once after game initialization is complete.
@@ -4346,6 +5090,14 @@ declare enum MessageEvents {
4346
5090
  * - `type`: 'keydown' | 'keyup'
4347
5091
  */
4348
5092
  KEY_EVENT = "PLAYCADEMY_KEY_EVENT",
5093
+ /**
5094
+ * Game requests platform to display an alert.
5095
+ * Sent when connection issues are detected or other important events occur.
5096
+ * Payload:
5097
+ * - `message`: string
5098
+ * - `options`: { type?: 'info' | 'warning' | 'error', duration?: number }
5099
+ */
5100
+ DISPLAY_ALERT = "PLAYCADEMY_DISPLAY_ALERT",
4349
5101
  /**
4350
5102
  * Notifies about authentication state changes.
4351
5103
  * Can be sent in both directions depending on auth flow.
@@ -4401,6 +5153,8 @@ type MessageEventMap = {
4401
5153
  [MessageEvents.FORCE_EXIT]: void;
4402
5154
  /** Overlay visibility state (true = show, false = hide) */
4403
5155
  [MessageEvents.OVERLAY]: boolean;
5156
+ /** Connection state change from platform */
5157
+ [MessageEvents.CONNECTION_STATE]: ConnectionStatePayload;
4404
5158
  /** Ready message has no payload data */
4405
5159
  [MessageEvents.READY]: void;
4406
5160
  /** Exit message has no payload data */
@@ -4409,6 +5163,8 @@ type MessageEventMap = {
4409
5163
  [MessageEvents.TELEMETRY]: TelemetryPayload;
4410
5164
  /** Key event data */
4411
5165
  [MessageEvents.KEY_EVENT]: KeyEventPayload;
5166
+ /** Display alert request from game */
5167
+ [MessageEvents.DISPLAY_ALERT]: DisplayAlertPayload;
4412
5168
  /** Authentication state change notification */
4413
5169
  [MessageEvents.AUTH_STATE_CHANGE]: AuthStateChangePayload;
4414
5170
  /** OAuth callback data from popup/new-tab windows */
@@ -4780,5 +5536,5 @@ declare class PlaycademyMessaging {
4780
5536
  */
4781
5537
  declare const messaging: PlaycademyMessaging;
4782
5538
 
4783
- export { MessageEvents, PlaycademyClient, messaging };
4784
- export type { DevUploadEvent, DevUploadHooks };
5539
+ export { ApiError, ConnectionManager, ConnectionMonitor, MessageEvents, PlaycademyClient, PlaycademyError, extractApiErrorInfo, messaging };
5540
+ export type { ApiErrorInfo, ConnectionMonitorConfig, ConnectionState, ConnectionStatePayload, DevUploadEvent, DevUploadHooks, DisconnectContext, DisconnectHandler, DisplayAlertPayload };