gameglue 4.0.0 → 4.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +275 -275
  3. package/babel.config.cjs +5 -5
  4. package/coverage/auth.js.html +525 -525
  5. package/coverage/base.css +224 -224
  6. package/coverage/block-navigation.js +87 -87
  7. package/coverage/favicon.png +0 -0
  8. package/coverage/index.html +175 -175
  9. package/coverage/index.js.html +309 -309
  10. package/coverage/lcov-report/auth.js.html +525 -525
  11. package/coverage/lcov-report/base.css +224 -224
  12. package/coverage/lcov-report/block-navigation.js +87 -87
  13. package/coverage/lcov-report/favicon.png +0 -0
  14. package/coverage/lcov-report/index.html +175 -175
  15. package/coverage/lcov-report/index.js.html +309 -309
  16. package/coverage/lcov-report/listener.js.html +528 -528
  17. package/coverage/lcov-report/prettify.css +1 -1
  18. package/coverage/lcov-report/prettify.js +2 -2
  19. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  20. package/coverage/lcov-report/sorter.js +210 -210
  21. package/coverage/lcov-report/user.js.html +117 -117
  22. package/coverage/lcov-report/utils.js.html +117 -117
  23. package/coverage/lcov.info +391 -391
  24. package/coverage/listener.js.html +528 -528
  25. package/coverage/prettify.css +1 -1
  26. package/coverage/prettify.js +2 -2
  27. package/coverage/sort-arrow-sprite.png +0 -0
  28. package/coverage/sorter.js +210 -210
  29. package/coverage/user.js.html +117 -117
  30. package/coverage/utils.js.html +117 -117
  31. package/dist/gg.cjs.js +1 -1
  32. package/dist/gg.cjs.js.map +1 -1
  33. package/dist/gg.esm.js +1 -1
  34. package/dist/gg.esm.js.map +1 -1
  35. package/dist/gg.umd.js +1 -1
  36. package/dist/gg.umd.js.map +1 -1
  37. package/examples/certs/cert.pem +19 -19
  38. package/examples/certs/key.pem +28 -28
  39. package/examples/flight-dashboard.html +431 -431
  40. package/examples/server.js +99 -99
  41. package/examples/telemetry-validator.html +1410 -1410
  42. package/jest.config.cjs +33 -33
  43. package/package.json +56 -56
  44. package/rollup.config.js +57 -57
  45. package/src/auth.js +255 -255
  46. package/src/auth.spec.js +481 -481
  47. package/src/index.js +168 -168
  48. package/src/listener.js +196 -193
  49. package/src/listener.spec.js +598 -598
  50. package/src/presence_listener.js +112 -112
  51. package/src/test/fixtures.js +106 -106
  52. package/src/test/setup.js +51 -51
  53. package/src/utils.js +63 -63
  54. package/src/utils.spec.js +78 -78
  55. package/types/index.d.ts +338 -338
  56. package/webpack.config.js +15 -15
package/src/index.js CHANGED
@@ -1,168 +1,168 @@
1
- import { GameGlueAuth } from './auth';
2
- import { io } from "socket.io-client";
3
- import { Listener } from "./listener";
4
- import { PresenceListener } from "./presence_listener";
5
- import { isCorsError, logCorsHelp } from './utils';
6
- import { getGameSchema, getCategorySchema, normalizeTelemetry } from '@gameglue/schemas';
7
-
8
- const GAME_IDS = {
9
- 'msfs': true,
10
- 'xplane': true,
11
- };
12
-
13
- const DEFAULT_SOCKET_URL = 'https://socks.gameglue.gg';
14
-
15
- class GameGlue extends GameGlueAuth {
16
- constructor(cfg) {
17
- super(cfg);
18
- this._socket = null;
19
- this._socketUrl = cfg.socketUrl || DEFAULT_SOCKET_URL;
20
- this._connectPromise = null;
21
- }
22
-
23
- /**
24
- * Create a listener for game telemetry.
25
- * Connects to socket server lazily on first call.
26
- * @param {Object} config - { userId, gameId, fields? }
27
- * @returns {Promise<Listener>}
28
- */
29
- async createListener(config) {
30
- if (!config) throw new Error('Not a valid listener config');
31
- if (!config.gameId || !GAME_IDS[config.gameId]) throw new Error('Not a valid Game ID');
32
- if (!config.userId) throw new Error('User ID not supplied');
33
- if (config.fields && !Array.isArray(config.fields)) throw new Error('fields must be an array');
34
-
35
- // Ensure socket is connected (lazy initialization)
36
- await this._ensureConnected();
37
-
38
- const listener = new Listener(this._socket, config);
39
- const establishConnectionResponse = await listener.establishConnection();
40
-
41
- // Handle reconnection
42
- this._socket.io.on('reconnect_attempt', () => {
43
- this._updateSocketAuth(this.getAccessToken());
44
- });
45
- this._socket.io.on('reconnect', () => {
46
- listener.establishConnection();
47
- });
48
-
49
- if (establishConnectionResponse.status !== 'success') {
50
- throw new Error(`There was a problem setting up the listener. Reason: ${establishConnectionResponse.reason}`);
51
- }
52
-
53
- return listener.setupEventListener();
54
- }
55
-
56
- /**
57
- * Create a presence listener for public broadcaster updates.
58
- * This does not require authentication.
59
- * @param {Object} config - { clientId, gameId }
60
- * @returns {Promise<PresenceListener>}
61
- */
62
- async createPresenceListener(config) {
63
- if (!config) throw new Error('Not a valid presence listener config');
64
- if (!config.gameId || !GAME_IDS[config.gameId]) throw new Error('Not a valid Game ID');
65
- if (!config.clientId) throw new Error('Client ID not supplied');
66
-
67
- const socket = io(this._socketUrl, {
68
- transports: ['websocket'],
69
- });
70
-
71
- await new Promise((resolve, reject) => {
72
- socket.on('connect', resolve);
73
- socket.on('connect_error', (err) => {
74
- if (isCorsError(err)) {
75
- logCorsHelp('WebSocket Connection', this._socketUrl);
76
- }
77
- reject(new Error(`Socket connection failed: ${err.message}`));
78
- });
79
- });
80
-
81
- const listener = new PresenceListener(socket, config);
82
- const establishConnectionResponse = await listener.establishConnection();
83
-
84
- // Handle reconnection
85
- socket.io.on('reconnect', () => {
86
- listener.establishConnection();
87
- });
88
-
89
- if (establishConnectionResponse.status !== 'success') {
90
- throw new Error(
91
- `There was a problem setting up the presence listener. Reason: ${establishConnectionResponse.reason}`
92
- );
93
- }
94
-
95
- return listener.setupEventListener();
96
- }
97
-
98
- // ============ Internal Methods ============
99
-
100
- async _ensureConnected() {
101
- // Already connected
102
- if (this._socket?.connected) {
103
- return;
104
- }
105
-
106
- // Connection in progress - wait for it
107
- if (this._connectPromise) {
108
- await this._connectPromise;
109
- return;
110
- }
111
-
112
- // Start new connection
113
- this._connectPromise = this._connect();
114
-
115
- try {
116
- await this._connectPromise;
117
- } finally {
118
- this._connectPromise = null;
119
- }
120
- }
121
-
122
- _connect() {
123
- return new Promise((resolve, reject) => {
124
- const token = this.getAccessToken();
125
-
126
- if (!token) {
127
- reject(new Error('Not authenticated - call isAuthenticated() first'));
128
- return;
129
- }
130
-
131
- this._socket = io(this._socketUrl, {
132
- transports: ['websocket'],
133
- auth: { token }
134
- });
135
-
136
- this._socket.on('connect', () => {
137
- resolve();
138
- });
139
-
140
- this._socket.on('connect_error', (err) => {
141
- if (isCorsError(err)) {
142
- logCorsHelp('WebSocket Connection', this._socketUrl);
143
- }
144
- reject(new Error(`Socket connection failed: ${err.message}`));
145
- });
146
-
147
- // Update socket auth when token refreshes
148
- this.onTokenRefreshed((newToken) => {
149
- this._updateSocketAuth(newToken);
150
- });
151
- });
152
- }
153
-
154
- _updateSocketAuth(authToken) {
155
- if (this._socket) {
156
- this._socket.auth.token = authToken;
157
- }
158
- }
159
- }
160
-
161
- if (typeof window !== 'undefined') {
162
- window.GameGlue = GameGlue;
163
- // Expose schema utilities for validation tools
164
- window.GameGlueSchemas = { getGameSchema, getCategorySchema, normalizeTelemetry };
165
- }
166
-
167
- export default GameGlue;
168
- export { GameGlue, getGameSchema, getCategorySchema, normalizeTelemetry };
1
+ import { GameGlueAuth } from './auth';
2
+ import { io } from "socket.io-client";
3
+ import { Listener } from "./listener";
4
+ import { PresenceListener } from "./presence_listener";
5
+ import { isCorsError, logCorsHelp } from './utils';
6
+ import { getGameSchema, getCategorySchema, normalizeTelemetry } from '@gameglue/schemas';
7
+
8
+ const GAME_IDS = {
9
+ 'msfs': true,
10
+ 'xplane': true,
11
+ };
12
+
13
+ const DEFAULT_SOCKET_URL = 'https://socks.gameglue.gg';
14
+
15
+ class GameGlue extends GameGlueAuth {
16
+ constructor(cfg) {
17
+ super(cfg);
18
+ this._socket = null;
19
+ this._socketUrl = cfg.socketUrl || DEFAULT_SOCKET_URL;
20
+ this._connectPromise = null;
21
+ }
22
+
23
+ /**
24
+ * Create a listener for game telemetry.
25
+ * Connects to socket server lazily on first call.
26
+ * @param {Object} config - { userId, gameId, fields? }
27
+ * @returns {Promise<Listener>}
28
+ */
29
+ async createListener(config) {
30
+ if (!config) throw new Error('Not a valid listener config');
31
+ if (!config.gameId || !GAME_IDS[config.gameId]) throw new Error('Not a valid Game ID');
32
+ if (!config.userId) throw new Error('User ID not supplied');
33
+ if (config.fields && !Array.isArray(config.fields)) throw new Error('fields must be an array');
34
+
35
+ // Ensure socket is connected (lazy initialization)
36
+ await this._ensureConnected();
37
+
38
+ const listener = new Listener(this._socket, config);
39
+ const establishConnectionResponse = await listener.establishConnection();
40
+
41
+ // Handle reconnection
42
+ this._socket.io.on('reconnect_attempt', () => {
43
+ this._updateSocketAuth(this.getAccessToken());
44
+ });
45
+ this._socket.io.on('reconnect', () => {
46
+ listener.establishConnection();
47
+ });
48
+
49
+ if (establishConnectionResponse.status !== 'success') {
50
+ throw new Error(`There was a problem setting up the listener. Reason: ${establishConnectionResponse.reason}`);
51
+ }
52
+
53
+ return listener.setupEventListener();
54
+ }
55
+
56
+ /**
57
+ * Create a presence listener for public broadcaster updates.
58
+ * This does not require authentication.
59
+ * @param {Object} config - { clientId, gameId }
60
+ * @returns {Promise<PresenceListener>}
61
+ */
62
+ async createPresenceListener(config) {
63
+ if (!config) throw new Error('Not a valid presence listener config');
64
+ if (!config.gameId || !GAME_IDS[config.gameId]) throw new Error('Not a valid Game ID');
65
+ if (!config.clientId) throw new Error('Client ID not supplied');
66
+
67
+ const socket = io(this._socketUrl, {
68
+ transports: ['websocket'],
69
+ });
70
+
71
+ await new Promise((resolve, reject) => {
72
+ socket.on('connect', resolve);
73
+ socket.on('connect_error', (err) => {
74
+ if (isCorsError(err)) {
75
+ logCorsHelp('WebSocket Connection', this._socketUrl);
76
+ }
77
+ reject(new Error(`Socket connection failed: ${err.message}`));
78
+ });
79
+ });
80
+
81
+ const listener = new PresenceListener(socket, config);
82
+ const establishConnectionResponse = await listener.establishConnection();
83
+
84
+ // Handle reconnection
85
+ socket.io.on('reconnect', () => {
86
+ listener.establishConnection();
87
+ });
88
+
89
+ if (establishConnectionResponse.status !== 'success') {
90
+ throw new Error(
91
+ `There was a problem setting up the presence listener. Reason: ${establishConnectionResponse.reason}`
92
+ );
93
+ }
94
+
95
+ return listener.setupEventListener();
96
+ }
97
+
98
+ // ============ Internal Methods ============
99
+
100
+ async _ensureConnected() {
101
+ // Already connected
102
+ if (this._socket?.connected) {
103
+ return;
104
+ }
105
+
106
+ // Connection in progress - wait for it
107
+ if (this._connectPromise) {
108
+ await this._connectPromise;
109
+ return;
110
+ }
111
+
112
+ // Start new connection
113
+ this._connectPromise = this._connect();
114
+
115
+ try {
116
+ await this._connectPromise;
117
+ } finally {
118
+ this._connectPromise = null;
119
+ }
120
+ }
121
+
122
+ _connect() {
123
+ return new Promise((resolve, reject) => {
124
+ const token = this.getAccessToken();
125
+
126
+ if (!token) {
127
+ reject(new Error('Not authenticated - call isAuthenticated() first'));
128
+ return;
129
+ }
130
+
131
+ this._socket = io(this._socketUrl, {
132
+ transports: ['websocket'],
133
+ auth: { token }
134
+ });
135
+
136
+ this._socket.on('connect', () => {
137
+ resolve();
138
+ });
139
+
140
+ this._socket.on('connect_error', (err) => {
141
+ if (isCorsError(err)) {
142
+ logCorsHelp('WebSocket Connection', this._socketUrl);
143
+ }
144
+ reject(new Error(`Socket connection failed: ${err.message}`));
145
+ });
146
+
147
+ // Update socket auth when token refreshes
148
+ this.onTokenRefreshed((newToken) => {
149
+ this._updateSocketAuth(newToken);
150
+ });
151
+ });
152
+ }
153
+
154
+ _updateSocketAuth(authToken) {
155
+ if (this._socket) {
156
+ this._socket.auth.token = authToken;
157
+ }
158
+ }
159
+ }
160
+
161
+ if (typeof window !== 'undefined') {
162
+ window.GameGlue = GameGlue;
163
+ // Expose schema utilities for validation tools
164
+ window.GameGlueSchemas = { getGameSchema, getCategorySchema, normalizeTelemetry };
165
+ }
166
+
167
+ export default GameGlue;
168
+ export { GameGlue, getGameSchema, getCategorySchema, normalizeTelemetry };