@beta-gamer/angular 0.1.2 → 0.1.4

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.js CHANGED
@@ -48,9 +48,8 @@ module.exports = __toCommonJS(index_exports);
48
48
  var import_core = require("@angular/core");
49
49
  var import_socket = require("socket.io-client");
50
50
  function decodeToken(token) {
51
- if (!token || typeof token !== "string") {
51
+ if (!token || typeof token !== "string")
52
52
  throw new Error("@beta-gamer/angular: token is required and must be a string");
53
- }
54
53
  const parts = token.split(".");
55
54
  if (parts.length < 2) throw new Error("@beta-gamer/angular: invalid session token format");
56
55
  const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
@@ -61,20 +60,33 @@ var BetaGamerService = class {
61
60
  constructor() {
62
61
  this._socket = null;
63
62
  this._token = "";
63
+ /** Decoded session token payload — null until `init()` is called */
64
64
  this.session = (0, import_core.signal)(null);
65
+ /** Live game state updated by server events */
65
66
  this.gameState = (0, import_core.signal)({ status: "pending", players: [] });
66
67
  }
67
68
  get token() {
68
69
  return this._token;
69
70
  }
71
+ /** The active socket.io connection — available after `init()` with `connectSocket=true` */
70
72
  get socket() {
71
73
  return this._socket;
72
74
  }
73
- init(token, serverUrl = "https://api.beta-gamer.com", socketPath = "/socket.io") {
75
+ /**
76
+ * @description Initialises the service with a session token.
77
+ * Decodes the token, sets up reactive state, and optionally connects the socket.
78
+ *
79
+ * @param token - JWT session token from your backend
80
+ * @param serverUrl - Base URL of the Beta Gamer server
81
+ * @param socketPath - socket.io path (default `/socket.io`)
82
+ * @param connectSocket - Set false to skip socket connection (e.g. for SSR or testing)
83
+ */
84
+ init(token, serverUrl = "https://api.beta-gamer.com", socketPath = "/socket.io", connectSocket = true) {
74
85
  this._token = token;
75
86
  const payload = decodeToken(token);
76
87
  this.session.set(payload);
77
88
  this.gameState.set({ status: "pending", players: payload.players });
89
+ if (!connectSocket) return;
78
90
  const s = (0, import_socket.io)(`${serverUrl}/${payload.game}`, {
79
91
  auth: { token },
80
92
  path: socketPath,
@@ -149,7 +161,7 @@ var TimerComponent = class {
149
161
  constructor() {
150
162
  this.player = "self";
151
163
  this.initialSeconds = 600;
152
- this.seconds = this.initialSeconds;
164
+ this.seconds = 600;
153
165
  this.interval = null;
154
166
  this.svc = (0, import_core3.inject)(BetaGamerService);
155
167
  }
@@ -160,20 +172,17 @@ var TimerComponent = class {
160
172
  }
161
173
  ngOnInit() {
162
174
  this.seconds = this.initialSeconds;
163
- const socket = this.svc["_socket"];
164
- if (socket) {
165
- socket.on("game:clock", (clocks) => {
166
- if (clocks[this.player] !== void 0) this.seconds = clocks[this.player];
167
- });
168
- }
175
+ this.svc.socket?.on("game:clock", (clocks) => {
176
+ if (clocks[this.player] !== void 0) this.seconds = clocks[this.player];
177
+ });
169
178
  this.interval = setInterval(() => {
170
- if (this.svc.gameState().status === "active") {
179
+ if (this.svc.gameState().status === "active")
171
180
  this.seconds = Math.max(0, this.seconds - 1);
172
- }
173
181
  }, 1e3);
174
182
  }
175
183
  ngOnDestroy() {
176
184
  if (this.interval) clearInterval(this.interval);
185
+ this.svc.socket?.off("game:clock");
177
186
  }
178
187
  };
179
188
  __decorateClass([
@@ -194,36 +203,93 @@ TimerComponent = __decorateClass([
194
203
  })
195
204
  ], TimerComponent);
196
205
 
197
- // src/components/chess/index.ts
206
+ // src/components/chess/ChessBoardComponent.ts
207
+ var import_core5 = require("@angular/core");
208
+
209
+ // src/components/shared/EmbedBoardBase.ts
198
210
  var import_core4 = require("@angular/core");
199
- var ChessBoardComponent = class {
211
+ var EmbedBoardBase = class {
200
212
  constructor() {
201
213
  this.serverUrl = "https://api.beta-gamer.com";
202
- this.showAfkWarning = true;
214
+ this.onLeave = new import_core4.EventEmitter();
203
215
  this.svc = (0, import_core4.inject)(BetaGamerService);
204
- }
205
- get embedUrl() {
206
- return this.showAfkWarning ? `${this.serverUrl}/embed/chess` : `${this.serverUrl}/embed/chess?afkWarning=0`;
216
+ this.messageHandler = (e) => {
217
+ try {
218
+ const data = typeof e.data === "string" ? JSON.parse(e.data) : e.data;
219
+ if (data?.type === "bg:leave") this.onLeave.emit();
220
+ } catch {
221
+ }
222
+ };
207
223
  }
208
224
  ngAfterViewInit() {
225
+ window.addEventListener("message", this.messageHandler);
209
226
  const iframe = this.iframeRef?.nativeElement;
210
227
  if (!iframe) return;
211
228
  iframe.addEventListener("load", () => {
212
229
  iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
213
230
  });
214
231
  }
232
+ ngOnDestroy() {
233
+ window.removeEventListener("message", this.messageHandler);
234
+ }
235
+ /** Builds a query string from a params object, omitting undefined values */
236
+ buildQuery(params) {
237
+ const p = new URLSearchParams();
238
+ for (const [k, v] of Object.entries(params)) if (v !== void 0) p.set(k, v);
239
+ const qs = p.toString();
240
+ return qs ? "?" + qs : "";
241
+ }
215
242
  };
216
243
  __decorateClass([
217
244
  (0, import_core4.Input)()
218
- ], ChessBoardComponent.prototype, "serverUrl", 2);
245
+ ], EmbedBoardBase.prototype, "serverUrl", 2);
219
246
  __decorateClass([
220
- (0, import_core4.Input)()
221
- ], ChessBoardComponent.prototype, "showAfkWarning", 2);
247
+ (0, import_core4.Output)()
248
+ ], EmbedBoardBase.prototype, "onLeave", 2);
222
249
  __decorateClass([
223
250
  (0, import_core4.ViewChild)("iframe")
224
- ], ChessBoardComponent.prototype, "iframeRef", 2);
251
+ ], EmbedBoardBase.prototype, "iframeRef", 2);
252
+ EmbedBoardBase = __decorateClass([
253
+ (0, import_core4.Directive)()
254
+ ], EmbedBoardBase);
255
+
256
+ // src/components/chess/ChessBoardComponent.ts
257
+ var ChessBoardComponent = class extends EmbedBoardBase {
258
+ constructor() {
259
+ super(...arguments);
260
+ this.showAfkWarning = true;
261
+ this.showCoords = true;
262
+ this.showLastMove = true;
263
+ this.showLegalMoves = true;
264
+ this.orientation = "auto";
265
+ }
266
+ get embedUrl() {
267
+ return this.serverUrl + "/embed/chess" + this.buildQuery({
268
+ afkWarning: !this.showAfkWarning ? "0" : void 0,
269
+ showCoords: !this.showCoords ? "0" : void 0,
270
+ showLastMove: !this.showLastMove ? "0" : void 0,
271
+ showLegalMoves: !this.showLegalMoves ? "0" : void 0,
272
+ orientation: this.orientation !== "auto" ? this.orientation : void 0
273
+ });
274
+ }
275
+ };
276
+ __decorateClass([
277
+ (0, import_core5.Input)()
278
+ ], ChessBoardComponent.prototype, "showAfkWarning", 2);
279
+ __decorateClass([
280
+ (0, import_core5.Input)()
281
+ ], ChessBoardComponent.prototype, "showCoords", 2);
282
+ __decorateClass([
283
+ (0, import_core5.Input)()
284
+ ], ChessBoardComponent.prototype, "showLastMove", 2);
285
+ __decorateClass([
286
+ (0, import_core5.Input)()
287
+ ], ChessBoardComponent.prototype, "showLegalMoves", 2);
288
+ __decorateClass([
289
+ (0, import_core5.Input)()
290
+ ], ChessBoardComponent.prototype, "orientation", 2);
225
291
  ChessBoardComponent = __decorateClass([
226
- (0, import_core4.Component)({
292
+ (0, import_core5.Component)({
227
293
  selector: "bg-chess-board",
228
294
  standalone: true,
229
295
  template: `
@@ -233,19 +299,25 @@ ChessBoardComponent = __decorateClass([
233
299
  `
234
300
  })
235
301
  ], ChessBoardComponent);
302
+
303
+ // src/components/chess/ChessMoveHistoryComponent.ts
304
+ var import_core6 = require("@angular/core");
236
305
  var ChessMoveHistoryComponent = class {
237
306
  };
238
307
  ChessMoveHistoryComponent = __decorateClass([
239
- (0, import_core4.Component)({
308
+ (0, import_core6.Component)({
240
309
  selector: "bg-chess-move-history",
241
310
  standalone: true,
242
311
  template: `<div data-role="move-history"></div>`
243
312
  })
244
313
  ], ChessMoveHistoryComponent);
314
+
315
+ // src/components/chess/ChessCapturedPiecesComponent.ts
316
+ var import_core7 = require("@angular/core");
245
317
  var ChessCapturedPiecesComponent = class {
246
318
  };
247
319
  ChessCapturedPiecesComponent = __decorateClass([
248
- (0, import_core4.Component)({
320
+ (0, import_core7.Component)({
249
321
  selector: "bg-chess-captured-pieces",
250
322
  standalone: true,
251
323
  template: `<div data-role="captured-pieces"></div>`
@@ -253,35 +325,33 @@ ChessCapturedPiecesComponent = __decorateClass([
253
325
  ], ChessCapturedPiecesComponent);
254
326
 
255
327
  // src/components/checkers/index.ts
256
- var import_core5 = require("@angular/core");
257
- var CheckersBoardComponent = class {
328
+ var import_core8 = require("@angular/core");
329
+ var CheckersBoardComponent = class extends EmbedBoardBase {
258
330
  constructor() {
259
- this.serverUrl = "https://api.beta-gamer.com";
331
+ super(...arguments);
260
332
  this.showAfkWarning = true;
261
- this.svc = (0, import_core5.inject)(BetaGamerService);
333
+ this.showLastMove = true;
334
+ this.orientation = "auto";
262
335
  }
263
336
  get embedUrl() {
264
- return this.showAfkWarning ? `${this.serverUrl}/embed/checkers` : `${this.serverUrl}/embed/checkers?afkWarning=0`;
265
- }
266
- ngAfterViewInit() {
267
- const iframe = this.iframeRef?.nativeElement;
268
- if (!iframe) return;
269
- iframe.addEventListener("load", () => {
270
- iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
337
+ return this.serverUrl + "/embed/checkers" + this.buildQuery({
338
+ afkWarning: !this.showAfkWarning ? "0" : void 0,
339
+ showLastMove: !this.showLastMove ? "0" : void 0,
340
+ orientation: this.orientation !== "auto" ? this.orientation : void 0
271
341
  });
272
342
  }
273
343
  };
274
344
  __decorateClass([
275
- (0, import_core5.Input)()
276
- ], CheckersBoardComponent.prototype, "serverUrl", 2);
277
- __decorateClass([
278
- (0, import_core5.Input)()
345
+ (0, import_core8.Input)()
279
346
  ], CheckersBoardComponent.prototype, "showAfkWarning", 2);
280
347
  __decorateClass([
281
- (0, import_core5.ViewChild)("iframe")
282
- ], CheckersBoardComponent.prototype, "iframeRef", 2);
348
+ (0, import_core8.Input)()
349
+ ], CheckersBoardComponent.prototype, "showLastMove", 2);
350
+ __decorateClass([
351
+ (0, import_core8.Input)()
352
+ ], CheckersBoardComponent.prototype, "orientation", 2);
283
353
  CheckersBoardComponent = __decorateClass([
284
- (0, import_core5.Component)({
354
+ (0, import_core8.Component)({
285
355
  selector: "bg-checkers-board",
286
356
  standalone: true,
287
357
  template: `
@@ -292,36 +362,24 @@ CheckersBoardComponent = __decorateClass([
292
362
  })
293
363
  ], CheckersBoardComponent);
294
364
 
295
- // src/components/connect4/index.ts
296
- var import_core6 = require("@angular/core");
297
- var Connect4BoardComponent = class {
365
+ // src/components/connect4/Connect4BoardComponent.ts
366
+ var import_core9 = require("@angular/core");
367
+ var Connect4BoardComponent = class extends EmbedBoardBase {
298
368
  constructor() {
299
- this.serverUrl = "https://api.beta-gamer.com";
369
+ super(...arguments);
300
370
  this.showAfkWarning = true;
301
- this.svc = (0, import_core6.inject)(BetaGamerService);
302
371
  }
303
372
  get embedUrl() {
304
- return this.showAfkWarning ? `${this.serverUrl}/embed/connect4` : `${this.serverUrl}/embed/connect4?afkWarning=0`;
305
- }
306
- ngAfterViewInit() {
307
- const iframe = this.iframeRef?.nativeElement;
308
- if (!iframe) return;
309
- iframe.addEventListener("load", () => {
310
- iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
373
+ return this.serverUrl + "/embed/connect4" + this.buildQuery({
374
+ afkWarning: !this.showAfkWarning ? "0" : void 0
311
375
  });
312
376
  }
313
377
  };
314
378
  __decorateClass([
315
- (0, import_core6.Input)()
316
- ], Connect4BoardComponent.prototype, "serverUrl", 2);
317
- __decorateClass([
318
- (0, import_core6.Input)()
379
+ (0, import_core9.Input)()
319
380
  ], Connect4BoardComponent.prototype, "showAfkWarning", 2);
320
- __decorateClass([
321
- (0, import_core6.ViewChild)("iframe")
322
- ], Connect4BoardComponent.prototype, "iframeRef", 2);
323
381
  Connect4BoardComponent = __decorateClass([
324
- (0, import_core6.Component)({
382
+ (0, import_core9.Component)({
325
383
  selector: "bg-connect4-board",
326
384
  standalone: true,
327
385
  template: `
@@ -331,9 +389,12 @@ Connect4BoardComponent = __decorateClass([
331
389
  `
332
390
  })
333
391
  ], Connect4BoardComponent);
392
+
393
+ // src/components/connect4/Connect4ScoreComponent.ts
394
+ var import_core10 = require("@angular/core");
334
395
  var Connect4ScoreComponent = class {
335
396
  constructor() {
336
- this.svc = (0, import_core6.inject)(BetaGamerService);
397
+ this.svc = (0, import_core10.inject)(BetaGamerService);
337
398
  }
338
399
  get scores() {
339
400
  const s = this.svc.gameState()?.scores ?? {};
@@ -341,7 +402,7 @@ var Connect4ScoreComponent = class {
341
402
  }
342
403
  };
343
404
  Connect4ScoreComponent = __decorateClass([
344
- (0, import_core6.Component)({
405
+ (0, import_core10.Component)({
345
406
  selector: "bg-connect4-score",
346
407
  standalone: true,
347
408
  template: `
@@ -355,35 +416,23 @@ Connect4ScoreComponent = __decorateClass([
355
416
  ], Connect4ScoreComponent);
356
417
 
357
418
  // src/components/tictactoe/index.ts
358
- var import_core7 = require("@angular/core");
359
- var TictactoeBoardComponent = class {
419
+ var import_core11 = require("@angular/core");
420
+ var TictactoeBoardComponent = class extends EmbedBoardBase {
360
421
  constructor() {
361
- this.serverUrl = "https://api.beta-gamer.com";
422
+ super(...arguments);
362
423
  this.showAfkWarning = true;
363
- this.svc = (0, import_core7.inject)(BetaGamerService);
364
424
  }
365
425
  get embedUrl() {
366
- return this.showAfkWarning ? `${this.serverUrl}/embed/tictactoe` : `${this.serverUrl}/embed/tictactoe?afkWarning=0`;
367
- }
368
- ngAfterViewInit() {
369
- const iframe = this.iframeRef?.nativeElement;
370
- if (!iframe) return;
371
- iframe.addEventListener("load", () => {
372
- iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
426
+ return this.serverUrl + "/embed/tictactoe" + this.buildQuery({
427
+ afkWarning: !this.showAfkWarning ? "0" : void 0
373
428
  });
374
429
  }
375
430
  };
376
431
  __decorateClass([
377
- (0, import_core7.Input)()
378
- ], TictactoeBoardComponent.prototype, "serverUrl", 2);
379
- __decorateClass([
380
- (0, import_core7.Input)()
432
+ (0, import_core11.Input)()
381
433
  ], TictactoeBoardComponent.prototype, "showAfkWarning", 2);
382
- __decorateClass([
383
- (0, import_core7.ViewChild)("iframe")
384
- ], TictactoeBoardComponent.prototype, "iframeRef", 2);
385
434
  TictactoeBoardComponent = __decorateClass([
386
- (0, import_core7.Component)({
435
+ (0, import_core11.Component)({
387
436
  selector: "bg-tictactoe-board",
388
437
  standalone: true,
389
438
  template: `
@@ -394,12 +443,12 @@ TictactoeBoardComponent = __decorateClass([
394
443
  })
395
444
  ], TictactoeBoardComponent);
396
445
 
397
- // src/components/subway-runner/index.ts
398
- var import_core8 = require("@angular/core");
446
+ // src/components/subway-runner/SubwayRunnerGameComponent.ts
447
+ var import_core12 = require("@angular/core");
399
448
  var SubwayRunnerGameComponent = class {
400
449
  constructor() {
401
450
  this.serverUrl = "https://api.beta-gamer.com";
402
- this.svc = (0, import_core8.inject)(BetaGamerService);
451
+ this.svc = (0, import_core12.inject)(BetaGamerService);
403
452
  }
404
453
  get embedUrl() {
405
454
  return `${this.serverUrl}/embed/subway-runner`;
@@ -413,13 +462,13 @@ var SubwayRunnerGameComponent = class {
413
462
  }
414
463
  };
415
464
  __decorateClass([
416
- (0, import_core8.Input)()
465
+ (0, import_core12.Input)()
417
466
  ], SubwayRunnerGameComponent.prototype, "serverUrl", 2);
418
467
  __decorateClass([
419
- (0, import_core8.ViewChild)("iframe")
468
+ (0, import_core12.ViewChild)("iframe")
420
469
  ], SubwayRunnerGameComponent.prototype, "iframeRef", 2);
421
470
  SubwayRunnerGameComponent = __decorateClass([
422
- (0, import_core8.Component)({
471
+ (0, import_core12.Component)({
423
472
  selector: "bg-subway-runner",
424
473
  standalone: true,
425
474
  template: `
@@ -429,31 +478,37 @@ SubwayRunnerGameComponent = __decorateClass([
429
478
  `
430
479
  })
431
480
  ], SubwayRunnerGameComponent);
481
+
482
+ // src/components/subway-runner/SubwayRunnerScoreComponent.ts
483
+ var import_core13 = require("@angular/core");
432
484
  var SubwayRunnerScoreComponent = class {
433
485
  constructor() {
434
- this.svc = (0, import_core8.inject)(BetaGamerService);
486
+ this.svc = (0, import_core13.inject)(BetaGamerService);
435
487
  }
436
488
  get score() {
437
489
  return this.svc.gameState()?.score ?? 0;
438
490
  }
439
491
  };
440
492
  SubwayRunnerScoreComponent = __decorateClass([
441
- (0, import_core8.Component)({
493
+ (0, import_core13.Component)({
442
494
  selector: "bg-subway-runner-score",
443
495
  standalone: true,
444
496
  template: `<div data-role="score"><span data-role="score-value">{{ score }}</span></div>`
445
497
  })
446
498
  ], SubwayRunnerScoreComponent);
499
+
500
+ // src/components/subway-runner/SubwayRunnerLivesComponent.ts
501
+ var import_core14 = require("@angular/core");
447
502
  var SubwayRunnerLivesComponent = class {
448
503
  constructor() {
449
- this.svc = (0, import_core8.inject)(BetaGamerService);
504
+ this.svc = (0, import_core14.inject)(BetaGamerService);
450
505
  }
451
506
  get lives() {
452
507
  return this.svc.gameState()?.lives ?? 0;
453
508
  }
454
509
  };
455
510
  SubwayRunnerLivesComponent = __decorateClass([
456
- (0, import_core8.Component)({
511
+ (0, import_core14.Component)({
457
512
  selector: "bg-subway-runner-lives",
458
513
  standalone: true,
459
514
  template: `<div data-role="lives"><span data-role="lives-value">{{ lives }}</span></div>`