@beta-gamer/angular 0.1.1
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.mts +129 -0
- package/dist/index.d.ts +129 -0
- package/dist/index.js +461 -0
- package/dist/index.mjs +425 -0
- package/package.json +42 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import * as _socket_io_component_emitter from '@socket.io/component-emitter';
|
|
2
|
+
import * as _angular_core from '@angular/core';
|
|
3
|
+
import { OnDestroy, OnInit, AfterViewInit, ElementRef } from '@angular/core';
|
|
4
|
+
import { Socket } from 'socket.io-client';
|
|
5
|
+
|
|
6
|
+
type GameType = 'chess' | 'checkers' | 'connect4' | 'tictactoe' | 'subway-runner';
|
|
7
|
+
type MatchType = 'matchmaking' | 'private' | 'bot';
|
|
8
|
+
type SessionMode = 'live' | 'test' | 'training';
|
|
9
|
+
type SessionStatus = 'pending' | 'active' | 'ended';
|
|
10
|
+
interface SessionPlayer {
|
|
11
|
+
id: string;
|
|
12
|
+
displayName: string;
|
|
13
|
+
}
|
|
14
|
+
interface SessionTheme {
|
|
15
|
+
primaryColor?: string;
|
|
16
|
+
backgroundColor?: string;
|
|
17
|
+
boardLight?: string;
|
|
18
|
+
boardDark?: string;
|
|
19
|
+
fontFamily?: string;
|
|
20
|
+
[key: string]: string | undefined;
|
|
21
|
+
}
|
|
22
|
+
interface SessionTokenPayload {
|
|
23
|
+
sessionId: string;
|
|
24
|
+
game: GameType;
|
|
25
|
+
mode: SessionMode;
|
|
26
|
+
matchType: MatchType;
|
|
27
|
+
tenantId: string;
|
|
28
|
+
players: SessionPlayer[];
|
|
29
|
+
roomCode: string | null;
|
|
30
|
+
theme: SessionTheme;
|
|
31
|
+
exp: number;
|
|
32
|
+
}
|
|
33
|
+
interface GameState {
|
|
34
|
+
status: SessionStatus;
|
|
35
|
+
players: SessionPlayer[];
|
|
36
|
+
currentTurn?: string;
|
|
37
|
+
winner?: string | null;
|
|
38
|
+
reason?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
declare class BetaGamerService implements OnDestroy {
|
|
42
|
+
private _socket;
|
|
43
|
+
private _token;
|
|
44
|
+
readonly session: _angular_core.WritableSignal<SessionTokenPayload | null>;
|
|
45
|
+
readonly gameState: _angular_core.WritableSignal<GameState>;
|
|
46
|
+
get token(): string;
|
|
47
|
+
get socket(): Socket<_socket_io_component_emitter.DefaultEventsMap, _socket_io_component_emitter.DefaultEventsMap> | null;
|
|
48
|
+
init(token: string, serverUrl?: string, socketPath?: string): void;
|
|
49
|
+
ngOnDestroy(): void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare class PlayerCardComponent {
|
|
53
|
+
player: 'self' | 'opponent';
|
|
54
|
+
private svc;
|
|
55
|
+
get displayName(): string;
|
|
56
|
+
get isActive(): boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare class TimerComponent implements OnInit, OnDestroy {
|
|
60
|
+
player: 'self' | 'opponent';
|
|
61
|
+
initialSeconds: number;
|
|
62
|
+
seconds: number;
|
|
63
|
+
private interval;
|
|
64
|
+
private svc;
|
|
65
|
+
get formatted(): string;
|
|
66
|
+
ngOnInit(): void;
|
|
67
|
+
ngOnDestroy(): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
declare class ChessBoardComponent implements AfterViewInit {
|
|
71
|
+
serverUrl: string;
|
|
72
|
+
iframeRef: ElementRef<HTMLIFrameElement>;
|
|
73
|
+
svc: BetaGamerService;
|
|
74
|
+
get embedUrl(): string;
|
|
75
|
+
ngAfterViewInit(): void;
|
|
76
|
+
}
|
|
77
|
+
declare class ChessMoveHistoryComponent {
|
|
78
|
+
}
|
|
79
|
+
declare class ChessCapturedPiecesComponent {
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
declare class CheckersBoardComponent implements AfterViewInit {
|
|
83
|
+
serverUrl: string;
|
|
84
|
+
iframeRef: ElementRef<HTMLIFrameElement>;
|
|
85
|
+
svc: BetaGamerService;
|
|
86
|
+
get embedUrl(): string;
|
|
87
|
+
ngAfterViewInit(): void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
declare class Connect4BoardComponent implements AfterViewInit {
|
|
91
|
+
serverUrl: string;
|
|
92
|
+
iframeRef: ElementRef<HTMLIFrameElement>;
|
|
93
|
+
svc: BetaGamerService;
|
|
94
|
+
get embedUrl(): string;
|
|
95
|
+
ngAfterViewInit(): void;
|
|
96
|
+
}
|
|
97
|
+
declare class Connect4ScoreComponent {
|
|
98
|
+
svc: BetaGamerService;
|
|
99
|
+
get scores(): {
|
|
100
|
+
id: string;
|
|
101
|
+
score: unknown;
|
|
102
|
+
}[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
declare class TictactoeBoardComponent implements AfterViewInit {
|
|
106
|
+
serverUrl: string;
|
|
107
|
+
iframeRef: ElementRef<HTMLIFrameElement>;
|
|
108
|
+
svc: BetaGamerService;
|
|
109
|
+
get embedUrl(): string;
|
|
110
|
+
ngAfterViewInit(): void;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
declare class SubwayRunnerGameComponent implements AfterViewInit {
|
|
114
|
+
serverUrl: string;
|
|
115
|
+
iframeRef: ElementRef<HTMLIFrameElement>;
|
|
116
|
+
svc: BetaGamerService;
|
|
117
|
+
get embedUrl(): string;
|
|
118
|
+
ngAfterViewInit(): void;
|
|
119
|
+
}
|
|
120
|
+
declare class SubwayRunnerScoreComponent {
|
|
121
|
+
svc: BetaGamerService;
|
|
122
|
+
get score(): any;
|
|
123
|
+
}
|
|
124
|
+
declare class SubwayRunnerLivesComponent {
|
|
125
|
+
svc: BetaGamerService;
|
|
126
|
+
get lives(): any;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { BetaGamerService, CheckersBoardComponent, ChessBoardComponent, ChessCapturedPiecesComponent, ChessMoveHistoryComponent, Connect4BoardComponent, Connect4ScoreComponent, type GameState, type GameType, type MatchType, PlayerCardComponent, type SessionMode, type SessionPlayer, type SessionStatus, type SessionTheme, type SessionTokenPayload, SubwayRunnerGameComponent, SubwayRunnerLivesComponent, SubwayRunnerScoreComponent, TictactoeBoardComponent, TimerComponent };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import * as _socket_io_component_emitter from '@socket.io/component-emitter';
|
|
2
|
+
import * as _angular_core from '@angular/core';
|
|
3
|
+
import { OnDestroy, OnInit, AfterViewInit, ElementRef } from '@angular/core';
|
|
4
|
+
import { Socket } from 'socket.io-client';
|
|
5
|
+
|
|
6
|
+
type GameType = 'chess' | 'checkers' | 'connect4' | 'tictactoe' | 'subway-runner';
|
|
7
|
+
type MatchType = 'matchmaking' | 'private' | 'bot';
|
|
8
|
+
type SessionMode = 'live' | 'test' | 'training';
|
|
9
|
+
type SessionStatus = 'pending' | 'active' | 'ended';
|
|
10
|
+
interface SessionPlayer {
|
|
11
|
+
id: string;
|
|
12
|
+
displayName: string;
|
|
13
|
+
}
|
|
14
|
+
interface SessionTheme {
|
|
15
|
+
primaryColor?: string;
|
|
16
|
+
backgroundColor?: string;
|
|
17
|
+
boardLight?: string;
|
|
18
|
+
boardDark?: string;
|
|
19
|
+
fontFamily?: string;
|
|
20
|
+
[key: string]: string | undefined;
|
|
21
|
+
}
|
|
22
|
+
interface SessionTokenPayload {
|
|
23
|
+
sessionId: string;
|
|
24
|
+
game: GameType;
|
|
25
|
+
mode: SessionMode;
|
|
26
|
+
matchType: MatchType;
|
|
27
|
+
tenantId: string;
|
|
28
|
+
players: SessionPlayer[];
|
|
29
|
+
roomCode: string | null;
|
|
30
|
+
theme: SessionTheme;
|
|
31
|
+
exp: number;
|
|
32
|
+
}
|
|
33
|
+
interface GameState {
|
|
34
|
+
status: SessionStatus;
|
|
35
|
+
players: SessionPlayer[];
|
|
36
|
+
currentTurn?: string;
|
|
37
|
+
winner?: string | null;
|
|
38
|
+
reason?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
declare class BetaGamerService implements OnDestroy {
|
|
42
|
+
private _socket;
|
|
43
|
+
private _token;
|
|
44
|
+
readonly session: _angular_core.WritableSignal<SessionTokenPayload | null>;
|
|
45
|
+
readonly gameState: _angular_core.WritableSignal<GameState>;
|
|
46
|
+
get token(): string;
|
|
47
|
+
get socket(): Socket<_socket_io_component_emitter.DefaultEventsMap, _socket_io_component_emitter.DefaultEventsMap> | null;
|
|
48
|
+
init(token: string, serverUrl?: string, socketPath?: string): void;
|
|
49
|
+
ngOnDestroy(): void;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
declare class PlayerCardComponent {
|
|
53
|
+
player: 'self' | 'opponent';
|
|
54
|
+
private svc;
|
|
55
|
+
get displayName(): string;
|
|
56
|
+
get isActive(): boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare class TimerComponent implements OnInit, OnDestroy {
|
|
60
|
+
player: 'self' | 'opponent';
|
|
61
|
+
initialSeconds: number;
|
|
62
|
+
seconds: number;
|
|
63
|
+
private interval;
|
|
64
|
+
private svc;
|
|
65
|
+
get formatted(): string;
|
|
66
|
+
ngOnInit(): void;
|
|
67
|
+
ngOnDestroy(): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
declare class ChessBoardComponent implements AfterViewInit {
|
|
71
|
+
serverUrl: string;
|
|
72
|
+
iframeRef: ElementRef<HTMLIFrameElement>;
|
|
73
|
+
svc: BetaGamerService;
|
|
74
|
+
get embedUrl(): string;
|
|
75
|
+
ngAfterViewInit(): void;
|
|
76
|
+
}
|
|
77
|
+
declare class ChessMoveHistoryComponent {
|
|
78
|
+
}
|
|
79
|
+
declare class ChessCapturedPiecesComponent {
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
declare class CheckersBoardComponent implements AfterViewInit {
|
|
83
|
+
serverUrl: string;
|
|
84
|
+
iframeRef: ElementRef<HTMLIFrameElement>;
|
|
85
|
+
svc: BetaGamerService;
|
|
86
|
+
get embedUrl(): string;
|
|
87
|
+
ngAfterViewInit(): void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
declare class Connect4BoardComponent implements AfterViewInit {
|
|
91
|
+
serverUrl: string;
|
|
92
|
+
iframeRef: ElementRef<HTMLIFrameElement>;
|
|
93
|
+
svc: BetaGamerService;
|
|
94
|
+
get embedUrl(): string;
|
|
95
|
+
ngAfterViewInit(): void;
|
|
96
|
+
}
|
|
97
|
+
declare class Connect4ScoreComponent {
|
|
98
|
+
svc: BetaGamerService;
|
|
99
|
+
get scores(): {
|
|
100
|
+
id: string;
|
|
101
|
+
score: unknown;
|
|
102
|
+
}[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
declare class TictactoeBoardComponent implements AfterViewInit {
|
|
106
|
+
serverUrl: string;
|
|
107
|
+
iframeRef: ElementRef<HTMLIFrameElement>;
|
|
108
|
+
svc: BetaGamerService;
|
|
109
|
+
get embedUrl(): string;
|
|
110
|
+
ngAfterViewInit(): void;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
declare class SubwayRunnerGameComponent implements AfterViewInit {
|
|
114
|
+
serverUrl: string;
|
|
115
|
+
iframeRef: ElementRef<HTMLIFrameElement>;
|
|
116
|
+
svc: BetaGamerService;
|
|
117
|
+
get embedUrl(): string;
|
|
118
|
+
ngAfterViewInit(): void;
|
|
119
|
+
}
|
|
120
|
+
declare class SubwayRunnerScoreComponent {
|
|
121
|
+
svc: BetaGamerService;
|
|
122
|
+
get score(): any;
|
|
123
|
+
}
|
|
124
|
+
declare class SubwayRunnerLivesComponent {
|
|
125
|
+
svc: BetaGamerService;
|
|
126
|
+
get lives(): any;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export { BetaGamerService, CheckersBoardComponent, ChessBoardComponent, ChessCapturedPiecesComponent, ChessMoveHistoryComponent, Connect4BoardComponent, Connect4ScoreComponent, type GameState, type GameType, type MatchType, PlayerCardComponent, type SessionMode, type SessionPlayer, type SessionStatus, type SessionTheme, type SessionTokenPayload, SubwayRunnerGameComponent, SubwayRunnerLivesComponent, SubwayRunnerScoreComponent, TictactoeBoardComponent, TimerComponent };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
20
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
21
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
22
|
+
if (decorator = decorators[i])
|
|
23
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
24
|
+
if (kind && result) __defProp(target, key, result);
|
|
25
|
+
return result;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// src/index.ts
|
|
29
|
+
var index_exports = {};
|
|
30
|
+
__export(index_exports, {
|
|
31
|
+
BetaGamerService: () => BetaGamerService,
|
|
32
|
+
CheckersBoardComponent: () => CheckersBoardComponent,
|
|
33
|
+
ChessBoardComponent: () => ChessBoardComponent,
|
|
34
|
+
ChessCapturedPiecesComponent: () => ChessCapturedPiecesComponent,
|
|
35
|
+
ChessMoveHistoryComponent: () => ChessMoveHistoryComponent,
|
|
36
|
+
Connect4BoardComponent: () => Connect4BoardComponent,
|
|
37
|
+
Connect4ScoreComponent: () => Connect4ScoreComponent,
|
|
38
|
+
PlayerCardComponent: () => PlayerCardComponent,
|
|
39
|
+
SubwayRunnerGameComponent: () => SubwayRunnerGameComponent,
|
|
40
|
+
SubwayRunnerLivesComponent: () => SubwayRunnerLivesComponent,
|
|
41
|
+
SubwayRunnerScoreComponent: () => SubwayRunnerScoreComponent,
|
|
42
|
+
TictactoeBoardComponent: () => TictactoeBoardComponent,
|
|
43
|
+
TimerComponent: () => TimerComponent
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(index_exports);
|
|
46
|
+
|
|
47
|
+
// src/service/BetaGamerService.ts
|
|
48
|
+
var import_core = require("@angular/core");
|
|
49
|
+
var import_socket = require("socket.io-client");
|
|
50
|
+
function decodeToken(token) {
|
|
51
|
+
if (!token || typeof token !== "string") {
|
|
52
|
+
throw new Error("@beta-gamer/angular: token is required and must be a string");
|
|
53
|
+
}
|
|
54
|
+
const parts = token.split(".");
|
|
55
|
+
if (parts.length < 2) throw new Error("@beta-gamer/angular: invalid session token format");
|
|
56
|
+
const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
57
|
+
const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
|
|
58
|
+
return JSON.parse(atob(padded));
|
|
59
|
+
}
|
|
60
|
+
var BetaGamerService = class {
|
|
61
|
+
constructor() {
|
|
62
|
+
this._socket = null;
|
|
63
|
+
this._token = "";
|
|
64
|
+
this.session = (0, import_core.signal)(null);
|
|
65
|
+
this.gameState = (0, import_core.signal)({ status: "pending", players: [] });
|
|
66
|
+
}
|
|
67
|
+
get token() {
|
|
68
|
+
return this._token;
|
|
69
|
+
}
|
|
70
|
+
get socket() {
|
|
71
|
+
return this._socket;
|
|
72
|
+
}
|
|
73
|
+
init(token, serverUrl = "https://api.beta-gamer.com", socketPath = "/socket.io") {
|
|
74
|
+
this._token = token;
|
|
75
|
+
const payload = decodeToken(token);
|
|
76
|
+
this.session.set(payload);
|
|
77
|
+
this.gameState.set({ status: "pending", players: payload.players });
|
|
78
|
+
const s = (0, import_socket.io)(`${serverUrl}/${payload.game}`, {
|
|
79
|
+
auth: { token },
|
|
80
|
+
path: socketPath,
|
|
81
|
+
transports: ["websocket", "polling"]
|
|
82
|
+
});
|
|
83
|
+
this._socket = s;
|
|
84
|
+
s.on("connect_error", (err) => {
|
|
85
|
+
const msg = err.message?.toLowerCase() ?? "";
|
|
86
|
+
let reason = "Unable to connect to the game server.";
|
|
87
|
+
if (msg.includes("websocket") || msg.includes("transport")) reason = "Connection failed: server does not accept WebSocket connections.";
|
|
88
|
+
else if (msg.includes("timeout")) reason = "Connection timed out. Check your internet connection.";
|
|
89
|
+
else if (msg.includes("unauthorized") || msg.includes("401")) reason = "Connection refused: invalid or expired session token.";
|
|
90
|
+
else if (msg.includes("not found") || msg.includes("404")) reason = "Game server not found. Check your serverUrl.";
|
|
91
|
+
else if (msg.includes("cors")) reason = "Connection blocked by CORS policy.";
|
|
92
|
+
else if (err.message) reason = `Connection error: ${err.message}`;
|
|
93
|
+
window.alert(`[Beta Gamer] ${reason}`);
|
|
94
|
+
});
|
|
95
|
+
s.on("game:state", (state) => {
|
|
96
|
+
this.gameState.update((prev) => ({ ...prev, ...state }));
|
|
97
|
+
});
|
|
98
|
+
s.on("game:over", ({ winner, reason }) => {
|
|
99
|
+
this.gameState.update((prev) => ({ ...prev, status: "ended", winner, reason }));
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
ngOnDestroy() {
|
|
103
|
+
this._socket?.disconnect();
|
|
104
|
+
this._socket = null;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
BetaGamerService = __decorateClass([
|
|
108
|
+
(0, import_core.Injectable)()
|
|
109
|
+
], BetaGamerService);
|
|
110
|
+
|
|
111
|
+
// src/components/PlayerCard.ts
|
|
112
|
+
var import_core2 = require("@angular/core");
|
|
113
|
+
var PlayerCardComponent = class {
|
|
114
|
+
constructor() {
|
|
115
|
+
this.player = "self";
|
|
116
|
+
this.svc = (0, import_core2.inject)(BetaGamerService);
|
|
117
|
+
}
|
|
118
|
+
get displayName() {
|
|
119
|
+
const players = this.svc.session()?.players ?? [];
|
|
120
|
+
return (this.player === "self" ? players[0] : players[1])?.displayName ?? "";
|
|
121
|
+
}
|
|
122
|
+
get isActive() {
|
|
123
|
+
const players = this.svc.session()?.players ?? [];
|
|
124
|
+
const p = this.player === "self" ? players[0] : players[1];
|
|
125
|
+
return p ? this.svc.gameState().currentTurn === p.id : false;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
__decorateClass([
|
|
129
|
+
(0, import_core2.Input)()
|
|
130
|
+
], PlayerCardComponent.prototype, "player", 2);
|
|
131
|
+
PlayerCardComponent = __decorateClass([
|
|
132
|
+
(0, import_core2.Component)({
|
|
133
|
+
selector: "bg-player-card",
|
|
134
|
+
standalone: true,
|
|
135
|
+
template: `
|
|
136
|
+
<div [attr.data-active]="isActive" [attr.data-player]="player">
|
|
137
|
+
<span data-role="display-name">{{ displayName }}</span>
|
|
138
|
+
@if (isActive) {
|
|
139
|
+
<span data-role="turn-indicator" aria-label="Your turn"></span>
|
|
140
|
+
}
|
|
141
|
+
</div>
|
|
142
|
+
`
|
|
143
|
+
})
|
|
144
|
+
], PlayerCardComponent);
|
|
145
|
+
|
|
146
|
+
// src/components/Timer.ts
|
|
147
|
+
var import_core3 = require("@angular/core");
|
|
148
|
+
var TimerComponent = class {
|
|
149
|
+
constructor() {
|
|
150
|
+
this.player = "self";
|
|
151
|
+
this.initialSeconds = 600;
|
|
152
|
+
this.seconds = this.initialSeconds;
|
|
153
|
+
this.interval = null;
|
|
154
|
+
this.svc = (0, import_core3.inject)(BetaGamerService);
|
|
155
|
+
}
|
|
156
|
+
get formatted() {
|
|
157
|
+
const m = Math.floor(this.seconds / 60).toString().padStart(2, "0");
|
|
158
|
+
const s = (this.seconds % 60).toString().padStart(2, "0");
|
|
159
|
+
return `${m}:${s}`;
|
|
160
|
+
}
|
|
161
|
+
ngOnInit() {
|
|
162
|
+
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
|
+
}
|
|
169
|
+
this.interval = setInterval(() => {
|
|
170
|
+
if (this.svc.gameState().status === "active") {
|
|
171
|
+
this.seconds = Math.max(0, this.seconds - 1);
|
|
172
|
+
}
|
|
173
|
+
}, 1e3);
|
|
174
|
+
}
|
|
175
|
+
ngOnDestroy() {
|
|
176
|
+
if (this.interval) clearInterval(this.interval);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
__decorateClass([
|
|
180
|
+
(0, import_core3.Input)()
|
|
181
|
+
], TimerComponent.prototype, "player", 2);
|
|
182
|
+
__decorateClass([
|
|
183
|
+
(0, import_core3.Input)()
|
|
184
|
+
], TimerComponent.prototype, "initialSeconds", 2);
|
|
185
|
+
TimerComponent = __decorateClass([
|
|
186
|
+
(0, import_core3.Component)({
|
|
187
|
+
selector: "bg-timer",
|
|
188
|
+
standalone: true,
|
|
189
|
+
template: `
|
|
190
|
+
<div [attr.data-player]="player" [attr.data-low]="seconds < 30">
|
|
191
|
+
<span data-role="clock">{{ formatted }}</span>
|
|
192
|
+
</div>
|
|
193
|
+
`
|
|
194
|
+
})
|
|
195
|
+
], TimerComponent);
|
|
196
|
+
|
|
197
|
+
// src/components/chess/index.ts
|
|
198
|
+
var import_core4 = require("@angular/core");
|
|
199
|
+
var ChessBoardComponent = class {
|
|
200
|
+
constructor() {
|
|
201
|
+
this.serverUrl = "https://api.beta-gamer.com";
|
|
202
|
+
this.svc = (0, import_core4.inject)(BetaGamerService);
|
|
203
|
+
}
|
|
204
|
+
get embedUrl() {
|
|
205
|
+
return `${this.serverUrl}/embed/chess`;
|
|
206
|
+
}
|
|
207
|
+
ngAfterViewInit() {
|
|
208
|
+
const iframe = this.iframeRef?.nativeElement;
|
|
209
|
+
if (!iframe) return;
|
|
210
|
+
iframe.addEventListener("load", () => {
|
|
211
|
+
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
__decorateClass([
|
|
216
|
+
(0, import_core4.Input)()
|
|
217
|
+
], ChessBoardComponent.prototype, "serverUrl", 2);
|
|
218
|
+
__decorateClass([
|
|
219
|
+
(0, import_core4.ViewChild)("iframe")
|
|
220
|
+
], ChessBoardComponent.prototype, "iframeRef", 2);
|
|
221
|
+
ChessBoardComponent = __decorateClass([
|
|
222
|
+
(0, import_core4.Component)({
|
|
223
|
+
selector: "bg-chess-board",
|
|
224
|
+
standalone: true,
|
|
225
|
+
template: `
|
|
226
|
+
@if (svc.session()?.game === 'chess') {
|
|
227
|
+
<iframe #iframe [src]="embedUrl" style="border:none;width:100%;height:100%" title="Chess" allow="autoplay"></iframe>
|
|
228
|
+
}
|
|
229
|
+
`
|
|
230
|
+
})
|
|
231
|
+
], ChessBoardComponent);
|
|
232
|
+
var ChessMoveHistoryComponent = class {
|
|
233
|
+
};
|
|
234
|
+
ChessMoveHistoryComponent = __decorateClass([
|
|
235
|
+
(0, import_core4.Component)({
|
|
236
|
+
selector: "bg-chess-move-history",
|
|
237
|
+
standalone: true,
|
|
238
|
+
template: `<div data-role="move-history"></div>`
|
|
239
|
+
})
|
|
240
|
+
], ChessMoveHistoryComponent);
|
|
241
|
+
var ChessCapturedPiecesComponent = class {
|
|
242
|
+
};
|
|
243
|
+
ChessCapturedPiecesComponent = __decorateClass([
|
|
244
|
+
(0, import_core4.Component)({
|
|
245
|
+
selector: "bg-chess-captured-pieces",
|
|
246
|
+
standalone: true,
|
|
247
|
+
template: `<div data-role="captured-pieces"></div>`
|
|
248
|
+
})
|
|
249
|
+
], ChessCapturedPiecesComponent);
|
|
250
|
+
|
|
251
|
+
// src/components/checkers/index.ts
|
|
252
|
+
var import_core5 = require("@angular/core");
|
|
253
|
+
var CheckersBoardComponent = class {
|
|
254
|
+
constructor() {
|
|
255
|
+
this.serverUrl = "https://api.beta-gamer.com";
|
|
256
|
+
this.svc = (0, import_core5.inject)(BetaGamerService);
|
|
257
|
+
}
|
|
258
|
+
get embedUrl() {
|
|
259
|
+
return `${this.serverUrl}/embed/checkers`;
|
|
260
|
+
}
|
|
261
|
+
ngAfterViewInit() {
|
|
262
|
+
const iframe = this.iframeRef?.nativeElement;
|
|
263
|
+
if (!iframe) return;
|
|
264
|
+
iframe.addEventListener("load", () => {
|
|
265
|
+
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
__decorateClass([
|
|
270
|
+
(0, import_core5.Input)()
|
|
271
|
+
], CheckersBoardComponent.prototype, "serverUrl", 2);
|
|
272
|
+
__decorateClass([
|
|
273
|
+
(0, import_core5.ViewChild)("iframe")
|
|
274
|
+
], CheckersBoardComponent.prototype, "iframeRef", 2);
|
|
275
|
+
CheckersBoardComponent = __decorateClass([
|
|
276
|
+
(0, import_core5.Component)({
|
|
277
|
+
selector: "bg-checkers-board",
|
|
278
|
+
standalone: true,
|
|
279
|
+
template: `
|
|
280
|
+
@if (svc.session()?.game === 'checkers') {
|
|
281
|
+
<iframe #iframe [src]="embedUrl" style="border:none;width:100%;height:100%" title="Checkers"></iframe>
|
|
282
|
+
}
|
|
283
|
+
`
|
|
284
|
+
})
|
|
285
|
+
], CheckersBoardComponent);
|
|
286
|
+
|
|
287
|
+
// src/components/connect4/index.ts
|
|
288
|
+
var import_core6 = require("@angular/core");
|
|
289
|
+
var Connect4BoardComponent = class {
|
|
290
|
+
constructor() {
|
|
291
|
+
this.serverUrl = "https://api.beta-gamer.com";
|
|
292
|
+
this.svc = (0, import_core6.inject)(BetaGamerService);
|
|
293
|
+
}
|
|
294
|
+
get embedUrl() {
|
|
295
|
+
return `${this.serverUrl}/embed/connect4`;
|
|
296
|
+
}
|
|
297
|
+
ngAfterViewInit() {
|
|
298
|
+
const iframe = this.iframeRef?.nativeElement;
|
|
299
|
+
if (!iframe) return;
|
|
300
|
+
iframe.addEventListener("load", () => {
|
|
301
|
+
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
__decorateClass([
|
|
306
|
+
(0, import_core6.Input)()
|
|
307
|
+
], Connect4BoardComponent.prototype, "serverUrl", 2);
|
|
308
|
+
__decorateClass([
|
|
309
|
+
(0, import_core6.ViewChild)("iframe")
|
|
310
|
+
], Connect4BoardComponent.prototype, "iframeRef", 2);
|
|
311
|
+
Connect4BoardComponent = __decorateClass([
|
|
312
|
+
(0, import_core6.Component)({
|
|
313
|
+
selector: "bg-connect4-board",
|
|
314
|
+
standalone: true,
|
|
315
|
+
template: `
|
|
316
|
+
@if (svc.session()?.game === 'connect4') {
|
|
317
|
+
<iframe #iframe [src]="embedUrl" style="border:none;width:100%;height:100%" title="Connect 4"></iframe>
|
|
318
|
+
}
|
|
319
|
+
`
|
|
320
|
+
})
|
|
321
|
+
], Connect4BoardComponent);
|
|
322
|
+
var Connect4ScoreComponent = class {
|
|
323
|
+
constructor() {
|
|
324
|
+
this.svc = (0, import_core6.inject)(BetaGamerService);
|
|
325
|
+
}
|
|
326
|
+
get scores() {
|
|
327
|
+
const s = this.svc.gameState()?.scores ?? {};
|
|
328
|
+
return Object.entries(s).map(([id, score]) => ({ id, score }));
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
Connect4ScoreComponent = __decorateClass([
|
|
332
|
+
(0, import_core6.Component)({
|
|
333
|
+
selector: "bg-connect4-score",
|
|
334
|
+
standalone: true,
|
|
335
|
+
template: `
|
|
336
|
+
<div data-role="score">
|
|
337
|
+
@for (entry of scores; track entry.id) {
|
|
338
|
+
<span [attr.data-player]="entry.id" data-role="score-value">{{ entry.score }}</span>
|
|
339
|
+
}
|
|
340
|
+
</div>
|
|
341
|
+
`
|
|
342
|
+
})
|
|
343
|
+
], Connect4ScoreComponent);
|
|
344
|
+
|
|
345
|
+
// src/components/tictactoe/index.ts
|
|
346
|
+
var import_core7 = require("@angular/core");
|
|
347
|
+
var TictactoeBoardComponent = class {
|
|
348
|
+
constructor() {
|
|
349
|
+
this.serverUrl = "https://api.beta-gamer.com";
|
|
350
|
+
this.svc = (0, import_core7.inject)(BetaGamerService);
|
|
351
|
+
}
|
|
352
|
+
get embedUrl() {
|
|
353
|
+
return `${this.serverUrl}/embed/tictactoe`;
|
|
354
|
+
}
|
|
355
|
+
ngAfterViewInit() {
|
|
356
|
+
const iframe = this.iframeRef?.nativeElement;
|
|
357
|
+
if (!iframe) return;
|
|
358
|
+
iframe.addEventListener("load", () => {
|
|
359
|
+
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
__decorateClass([
|
|
364
|
+
(0, import_core7.Input)()
|
|
365
|
+
], TictactoeBoardComponent.prototype, "serverUrl", 2);
|
|
366
|
+
__decorateClass([
|
|
367
|
+
(0, import_core7.ViewChild)("iframe")
|
|
368
|
+
], TictactoeBoardComponent.prototype, "iframeRef", 2);
|
|
369
|
+
TictactoeBoardComponent = __decorateClass([
|
|
370
|
+
(0, import_core7.Component)({
|
|
371
|
+
selector: "bg-tictactoe-board",
|
|
372
|
+
standalone: true,
|
|
373
|
+
template: `
|
|
374
|
+
@if (svc.session()?.game === 'tictactoe') {
|
|
375
|
+
<iframe #iframe [src]="embedUrl" style="border:none;width:100%;height:100%" title="Tic-tac-toe"></iframe>
|
|
376
|
+
}
|
|
377
|
+
`
|
|
378
|
+
})
|
|
379
|
+
], TictactoeBoardComponent);
|
|
380
|
+
|
|
381
|
+
// src/components/subway-runner/index.ts
|
|
382
|
+
var import_core8 = require("@angular/core");
|
|
383
|
+
var SubwayRunnerGameComponent = class {
|
|
384
|
+
constructor() {
|
|
385
|
+
this.serverUrl = "https://api.beta-gamer.com";
|
|
386
|
+
this.svc = (0, import_core8.inject)(BetaGamerService);
|
|
387
|
+
}
|
|
388
|
+
get embedUrl() {
|
|
389
|
+
return `${this.serverUrl}/embed/subway-runner`;
|
|
390
|
+
}
|
|
391
|
+
ngAfterViewInit() {
|
|
392
|
+
const iframe = this.iframeRef?.nativeElement;
|
|
393
|
+
if (!iframe) return;
|
|
394
|
+
iframe.addEventListener("load", () => {
|
|
395
|
+
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
__decorateClass([
|
|
400
|
+
(0, import_core8.Input)()
|
|
401
|
+
], SubwayRunnerGameComponent.prototype, "serverUrl", 2);
|
|
402
|
+
__decorateClass([
|
|
403
|
+
(0, import_core8.ViewChild)("iframe")
|
|
404
|
+
], SubwayRunnerGameComponent.prototype, "iframeRef", 2);
|
|
405
|
+
SubwayRunnerGameComponent = __decorateClass([
|
|
406
|
+
(0, import_core8.Component)({
|
|
407
|
+
selector: "bg-subway-runner",
|
|
408
|
+
standalone: true,
|
|
409
|
+
template: `
|
|
410
|
+
@if (svc.session()?.game === 'subway-runner') {
|
|
411
|
+
<iframe #iframe [src]="embedUrl" style="border:none;width:100%;height:100%" title="Subway Runner"></iframe>
|
|
412
|
+
}
|
|
413
|
+
`
|
|
414
|
+
})
|
|
415
|
+
], SubwayRunnerGameComponent);
|
|
416
|
+
var SubwayRunnerScoreComponent = class {
|
|
417
|
+
constructor() {
|
|
418
|
+
this.svc = (0, import_core8.inject)(BetaGamerService);
|
|
419
|
+
}
|
|
420
|
+
get score() {
|
|
421
|
+
return this.svc.gameState()?.score ?? 0;
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
SubwayRunnerScoreComponent = __decorateClass([
|
|
425
|
+
(0, import_core8.Component)({
|
|
426
|
+
selector: "bg-subway-runner-score",
|
|
427
|
+
standalone: true,
|
|
428
|
+
template: `<div data-role="score"><span data-role="score-value">{{ score }}</span></div>`
|
|
429
|
+
})
|
|
430
|
+
], SubwayRunnerScoreComponent);
|
|
431
|
+
var SubwayRunnerLivesComponent = class {
|
|
432
|
+
constructor() {
|
|
433
|
+
this.svc = (0, import_core8.inject)(BetaGamerService);
|
|
434
|
+
}
|
|
435
|
+
get lives() {
|
|
436
|
+
return this.svc.gameState()?.lives ?? 0;
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
SubwayRunnerLivesComponent = __decorateClass([
|
|
440
|
+
(0, import_core8.Component)({
|
|
441
|
+
selector: "bg-subway-runner-lives",
|
|
442
|
+
standalone: true,
|
|
443
|
+
template: `<div data-role="lives"><span data-role="lives-value">{{ lives }}</span></div>`
|
|
444
|
+
})
|
|
445
|
+
], SubwayRunnerLivesComponent);
|
|
446
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
447
|
+
0 && (module.exports = {
|
|
448
|
+
BetaGamerService,
|
|
449
|
+
CheckersBoardComponent,
|
|
450
|
+
ChessBoardComponent,
|
|
451
|
+
ChessCapturedPiecesComponent,
|
|
452
|
+
ChessMoveHistoryComponent,
|
|
453
|
+
Connect4BoardComponent,
|
|
454
|
+
Connect4ScoreComponent,
|
|
455
|
+
PlayerCardComponent,
|
|
456
|
+
SubwayRunnerGameComponent,
|
|
457
|
+
SubwayRunnerLivesComponent,
|
|
458
|
+
SubwayRunnerScoreComponent,
|
|
459
|
+
TictactoeBoardComponent,
|
|
460
|
+
TimerComponent
|
|
461
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
4
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
5
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
6
|
+
if (decorator = decorators[i])
|
|
7
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
8
|
+
if (kind && result) __defProp(target, key, result);
|
|
9
|
+
return result;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/service/BetaGamerService.ts
|
|
13
|
+
import { Injectable, signal } from "@angular/core";
|
|
14
|
+
import { io } from "socket.io-client";
|
|
15
|
+
function decodeToken(token) {
|
|
16
|
+
if (!token || typeof token !== "string") {
|
|
17
|
+
throw new Error("@beta-gamer/angular: token is required and must be a string");
|
|
18
|
+
}
|
|
19
|
+
const parts = token.split(".");
|
|
20
|
+
if (parts.length < 2) throw new Error("@beta-gamer/angular: invalid session token format");
|
|
21
|
+
const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
22
|
+
const padded = base64.padEnd(base64.length + (4 - base64.length % 4) % 4, "=");
|
|
23
|
+
return JSON.parse(atob(padded));
|
|
24
|
+
}
|
|
25
|
+
var BetaGamerService = class {
|
|
26
|
+
constructor() {
|
|
27
|
+
this._socket = null;
|
|
28
|
+
this._token = "";
|
|
29
|
+
this.session = signal(null);
|
|
30
|
+
this.gameState = signal({ status: "pending", players: [] });
|
|
31
|
+
}
|
|
32
|
+
get token() {
|
|
33
|
+
return this._token;
|
|
34
|
+
}
|
|
35
|
+
get socket() {
|
|
36
|
+
return this._socket;
|
|
37
|
+
}
|
|
38
|
+
init(token, serverUrl = "https://api.beta-gamer.com", socketPath = "/socket.io") {
|
|
39
|
+
this._token = token;
|
|
40
|
+
const payload = decodeToken(token);
|
|
41
|
+
this.session.set(payload);
|
|
42
|
+
this.gameState.set({ status: "pending", players: payload.players });
|
|
43
|
+
const s = io(`${serverUrl}/${payload.game}`, {
|
|
44
|
+
auth: { token },
|
|
45
|
+
path: socketPath,
|
|
46
|
+
transports: ["websocket", "polling"]
|
|
47
|
+
});
|
|
48
|
+
this._socket = s;
|
|
49
|
+
s.on("connect_error", (err) => {
|
|
50
|
+
const msg = err.message?.toLowerCase() ?? "";
|
|
51
|
+
let reason = "Unable to connect to the game server.";
|
|
52
|
+
if (msg.includes("websocket") || msg.includes("transport")) reason = "Connection failed: server does not accept WebSocket connections.";
|
|
53
|
+
else if (msg.includes("timeout")) reason = "Connection timed out. Check your internet connection.";
|
|
54
|
+
else if (msg.includes("unauthorized") || msg.includes("401")) reason = "Connection refused: invalid or expired session token.";
|
|
55
|
+
else if (msg.includes("not found") || msg.includes("404")) reason = "Game server not found. Check your serverUrl.";
|
|
56
|
+
else if (msg.includes("cors")) reason = "Connection blocked by CORS policy.";
|
|
57
|
+
else if (err.message) reason = `Connection error: ${err.message}`;
|
|
58
|
+
window.alert(`[Beta Gamer] ${reason}`);
|
|
59
|
+
});
|
|
60
|
+
s.on("game:state", (state) => {
|
|
61
|
+
this.gameState.update((prev) => ({ ...prev, ...state }));
|
|
62
|
+
});
|
|
63
|
+
s.on("game:over", ({ winner, reason }) => {
|
|
64
|
+
this.gameState.update((prev) => ({ ...prev, status: "ended", winner, reason }));
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
ngOnDestroy() {
|
|
68
|
+
this._socket?.disconnect();
|
|
69
|
+
this._socket = null;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
BetaGamerService = __decorateClass([
|
|
73
|
+
Injectable()
|
|
74
|
+
], BetaGamerService);
|
|
75
|
+
|
|
76
|
+
// src/components/PlayerCard.ts
|
|
77
|
+
import { Component, Input, inject } from "@angular/core";
|
|
78
|
+
var PlayerCardComponent = class {
|
|
79
|
+
constructor() {
|
|
80
|
+
this.player = "self";
|
|
81
|
+
this.svc = inject(BetaGamerService);
|
|
82
|
+
}
|
|
83
|
+
get displayName() {
|
|
84
|
+
const players = this.svc.session()?.players ?? [];
|
|
85
|
+
return (this.player === "self" ? players[0] : players[1])?.displayName ?? "";
|
|
86
|
+
}
|
|
87
|
+
get isActive() {
|
|
88
|
+
const players = this.svc.session()?.players ?? [];
|
|
89
|
+
const p = this.player === "self" ? players[0] : players[1];
|
|
90
|
+
return p ? this.svc.gameState().currentTurn === p.id : false;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
__decorateClass([
|
|
94
|
+
Input()
|
|
95
|
+
], PlayerCardComponent.prototype, "player", 2);
|
|
96
|
+
PlayerCardComponent = __decorateClass([
|
|
97
|
+
Component({
|
|
98
|
+
selector: "bg-player-card",
|
|
99
|
+
standalone: true,
|
|
100
|
+
template: `
|
|
101
|
+
<div [attr.data-active]="isActive" [attr.data-player]="player">
|
|
102
|
+
<span data-role="display-name">{{ displayName }}</span>
|
|
103
|
+
@if (isActive) {
|
|
104
|
+
<span data-role="turn-indicator" aria-label="Your turn"></span>
|
|
105
|
+
}
|
|
106
|
+
</div>
|
|
107
|
+
`
|
|
108
|
+
})
|
|
109
|
+
], PlayerCardComponent);
|
|
110
|
+
|
|
111
|
+
// src/components/Timer.ts
|
|
112
|
+
import { Component as Component2, Input as Input2, inject as inject2 } from "@angular/core";
|
|
113
|
+
var TimerComponent = class {
|
|
114
|
+
constructor() {
|
|
115
|
+
this.player = "self";
|
|
116
|
+
this.initialSeconds = 600;
|
|
117
|
+
this.seconds = this.initialSeconds;
|
|
118
|
+
this.interval = null;
|
|
119
|
+
this.svc = inject2(BetaGamerService);
|
|
120
|
+
}
|
|
121
|
+
get formatted() {
|
|
122
|
+
const m = Math.floor(this.seconds / 60).toString().padStart(2, "0");
|
|
123
|
+
const s = (this.seconds % 60).toString().padStart(2, "0");
|
|
124
|
+
return `${m}:${s}`;
|
|
125
|
+
}
|
|
126
|
+
ngOnInit() {
|
|
127
|
+
this.seconds = this.initialSeconds;
|
|
128
|
+
const socket = this.svc["_socket"];
|
|
129
|
+
if (socket) {
|
|
130
|
+
socket.on("game:clock", (clocks) => {
|
|
131
|
+
if (clocks[this.player] !== void 0) this.seconds = clocks[this.player];
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
this.interval = setInterval(() => {
|
|
135
|
+
if (this.svc.gameState().status === "active") {
|
|
136
|
+
this.seconds = Math.max(0, this.seconds - 1);
|
|
137
|
+
}
|
|
138
|
+
}, 1e3);
|
|
139
|
+
}
|
|
140
|
+
ngOnDestroy() {
|
|
141
|
+
if (this.interval) clearInterval(this.interval);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
__decorateClass([
|
|
145
|
+
Input2()
|
|
146
|
+
], TimerComponent.prototype, "player", 2);
|
|
147
|
+
__decorateClass([
|
|
148
|
+
Input2()
|
|
149
|
+
], TimerComponent.prototype, "initialSeconds", 2);
|
|
150
|
+
TimerComponent = __decorateClass([
|
|
151
|
+
Component2({
|
|
152
|
+
selector: "bg-timer",
|
|
153
|
+
standalone: true,
|
|
154
|
+
template: `
|
|
155
|
+
<div [attr.data-player]="player" [attr.data-low]="seconds < 30">
|
|
156
|
+
<span data-role="clock">{{ formatted }}</span>
|
|
157
|
+
</div>
|
|
158
|
+
`
|
|
159
|
+
})
|
|
160
|
+
], TimerComponent);
|
|
161
|
+
|
|
162
|
+
// src/components/chess/index.ts
|
|
163
|
+
import { Component as Component3, Input as Input3, ViewChild, inject as inject3 } from "@angular/core";
|
|
164
|
+
var ChessBoardComponent = class {
|
|
165
|
+
constructor() {
|
|
166
|
+
this.serverUrl = "https://api.beta-gamer.com";
|
|
167
|
+
this.svc = inject3(BetaGamerService);
|
|
168
|
+
}
|
|
169
|
+
get embedUrl() {
|
|
170
|
+
return `${this.serverUrl}/embed/chess`;
|
|
171
|
+
}
|
|
172
|
+
ngAfterViewInit() {
|
|
173
|
+
const iframe = this.iframeRef?.nativeElement;
|
|
174
|
+
if (!iframe) return;
|
|
175
|
+
iframe.addEventListener("load", () => {
|
|
176
|
+
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
__decorateClass([
|
|
181
|
+
Input3()
|
|
182
|
+
], ChessBoardComponent.prototype, "serverUrl", 2);
|
|
183
|
+
__decorateClass([
|
|
184
|
+
ViewChild("iframe")
|
|
185
|
+
], ChessBoardComponent.prototype, "iframeRef", 2);
|
|
186
|
+
ChessBoardComponent = __decorateClass([
|
|
187
|
+
Component3({
|
|
188
|
+
selector: "bg-chess-board",
|
|
189
|
+
standalone: true,
|
|
190
|
+
template: `
|
|
191
|
+
@if (svc.session()?.game === 'chess') {
|
|
192
|
+
<iframe #iframe [src]="embedUrl" style="border:none;width:100%;height:100%" title="Chess" allow="autoplay"></iframe>
|
|
193
|
+
}
|
|
194
|
+
`
|
|
195
|
+
})
|
|
196
|
+
], ChessBoardComponent);
|
|
197
|
+
var ChessMoveHistoryComponent = class {
|
|
198
|
+
};
|
|
199
|
+
ChessMoveHistoryComponent = __decorateClass([
|
|
200
|
+
Component3({
|
|
201
|
+
selector: "bg-chess-move-history",
|
|
202
|
+
standalone: true,
|
|
203
|
+
template: `<div data-role="move-history"></div>`
|
|
204
|
+
})
|
|
205
|
+
], ChessMoveHistoryComponent);
|
|
206
|
+
var ChessCapturedPiecesComponent = class {
|
|
207
|
+
};
|
|
208
|
+
ChessCapturedPiecesComponent = __decorateClass([
|
|
209
|
+
Component3({
|
|
210
|
+
selector: "bg-chess-captured-pieces",
|
|
211
|
+
standalone: true,
|
|
212
|
+
template: `<div data-role="captured-pieces"></div>`
|
|
213
|
+
})
|
|
214
|
+
], ChessCapturedPiecesComponent);
|
|
215
|
+
|
|
216
|
+
// src/components/checkers/index.ts
|
|
217
|
+
import { Component as Component4, Input as Input4, ViewChild as ViewChild2, inject as inject4 } from "@angular/core";
|
|
218
|
+
var CheckersBoardComponent = class {
|
|
219
|
+
constructor() {
|
|
220
|
+
this.serverUrl = "https://api.beta-gamer.com";
|
|
221
|
+
this.svc = inject4(BetaGamerService);
|
|
222
|
+
}
|
|
223
|
+
get embedUrl() {
|
|
224
|
+
return `${this.serverUrl}/embed/checkers`;
|
|
225
|
+
}
|
|
226
|
+
ngAfterViewInit() {
|
|
227
|
+
const iframe = this.iframeRef?.nativeElement;
|
|
228
|
+
if (!iframe) return;
|
|
229
|
+
iframe.addEventListener("load", () => {
|
|
230
|
+
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
__decorateClass([
|
|
235
|
+
Input4()
|
|
236
|
+
], CheckersBoardComponent.prototype, "serverUrl", 2);
|
|
237
|
+
__decorateClass([
|
|
238
|
+
ViewChild2("iframe")
|
|
239
|
+
], CheckersBoardComponent.prototype, "iframeRef", 2);
|
|
240
|
+
CheckersBoardComponent = __decorateClass([
|
|
241
|
+
Component4({
|
|
242
|
+
selector: "bg-checkers-board",
|
|
243
|
+
standalone: true,
|
|
244
|
+
template: `
|
|
245
|
+
@if (svc.session()?.game === 'checkers') {
|
|
246
|
+
<iframe #iframe [src]="embedUrl" style="border:none;width:100%;height:100%" title="Checkers"></iframe>
|
|
247
|
+
}
|
|
248
|
+
`
|
|
249
|
+
})
|
|
250
|
+
], CheckersBoardComponent);
|
|
251
|
+
|
|
252
|
+
// src/components/connect4/index.ts
|
|
253
|
+
import { Component as Component5, Input as Input5, ViewChild as ViewChild3, inject as inject5 } from "@angular/core";
|
|
254
|
+
var Connect4BoardComponent = class {
|
|
255
|
+
constructor() {
|
|
256
|
+
this.serverUrl = "https://api.beta-gamer.com";
|
|
257
|
+
this.svc = inject5(BetaGamerService);
|
|
258
|
+
}
|
|
259
|
+
get embedUrl() {
|
|
260
|
+
return `${this.serverUrl}/embed/connect4`;
|
|
261
|
+
}
|
|
262
|
+
ngAfterViewInit() {
|
|
263
|
+
const iframe = this.iframeRef?.nativeElement;
|
|
264
|
+
if (!iframe) return;
|
|
265
|
+
iframe.addEventListener("load", () => {
|
|
266
|
+
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
__decorateClass([
|
|
271
|
+
Input5()
|
|
272
|
+
], Connect4BoardComponent.prototype, "serverUrl", 2);
|
|
273
|
+
__decorateClass([
|
|
274
|
+
ViewChild3("iframe")
|
|
275
|
+
], Connect4BoardComponent.prototype, "iframeRef", 2);
|
|
276
|
+
Connect4BoardComponent = __decorateClass([
|
|
277
|
+
Component5({
|
|
278
|
+
selector: "bg-connect4-board",
|
|
279
|
+
standalone: true,
|
|
280
|
+
template: `
|
|
281
|
+
@if (svc.session()?.game === 'connect4') {
|
|
282
|
+
<iframe #iframe [src]="embedUrl" style="border:none;width:100%;height:100%" title="Connect 4"></iframe>
|
|
283
|
+
}
|
|
284
|
+
`
|
|
285
|
+
})
|
|
286
|
+
], Connect4BoardComponent);
|
|
287
|
+
var Connect4ScoreComponent = class {
|
|
288
|
+
constructor() {
|
|
289
|
+
this.svc = inject5(BetaGamerService);
|
|
290
|
+
}
|
|
291
|
+
get scores() {
|
|
292
|
+
const s = this.svc.gameState()?.scores ?? {};
|
|
293
|
+
return Object.entries(s).map(([id, score]) => ({ id, score }));
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
Connect4ScoreComponent = __decorateClass([
|
|
297
|
+
Component5({
|
|
298
|
+
selector: "bg-connect4-score",
|
|
299
|
+
standalone: true,
|
|
300
|
+
template: `
|
|
301
|
+
<div data-role="score">
|
|
302
|
+
@for (entry of scores; track entry.id) {
|
|
303
|
+
<span [attr.data-player]="entry.id" data-role="score-value">{{ entry.score }}</span>
|
|
304
|
+
}
|
|
305
|
+
</div>
|
|
306
|
+
`
|
|
307
|
+
})
|
|
308
|
+
], Connect4ScoreComponent);
|
|
309
|
+
|
|
310
|
+
// src/components/tictactoe/index.ts
|
|
311
|
+
import { Component as Component6, Input as Input6, ViewChild as ViewChild4, inject as inject6 } from "@angular/core";
|
|
312
|
+
var TictactoeBoardComponent = class {
|
|
313
|
+
constructor() {
|
|
314
|
+
this.serverUrl = "https://api.beta-gamer.com";
|
|
315
|
+
this.svc = inject6(BetaGamerService);
|
|
316
|
+
}
|
|
317
|
+
get embedUrl() {
|
|
318
|
+
return `${this.serverUrl}/embed/tictactoe`;
|
|
319
|
+
}
|
|
320
|
+
ngAfterViewInit() {
|
|
321
|
+
const iframe = this.iframeRef?.nativeElement;
|
|
322
|
+
if (!iframe) return;
|
|
323
|
+
iframe.addEventListener("load", () => {
|
|
324
|
+
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
__decorateClass([
|
|
329
|
+
Input6()
|
|
330
|
+
], TictactoeBoardComponent.prototype, "serverUrl", 2);
|
|
331
|
+
__decorateClass([
|
|
332
|
+
ViewChild4("iframe")
|
|
333
|
+
], TictactoeBoardComponent.prototype, "iframeRef", 2);
|
|
334
|
+
TictactoeBoardComponent = __decorateClass([
|
|
335
|
+
Component6({
|
|
336
|
+
selector: "bg-tictactoe-board",
|
|
337
|
+
standalone: true,
|
|
338
|
+
template: `
|
|
339
|
+
@if (svc.session()?.game === 'tictactoe') {
|
|
340
|
+
<iframe #iframe [src]="embedUrl" style="border:none;width:100%;height:100%" title="Tic-tac-toe"></iframe>
|
|
341
|
+
}
|
|
342
|
+
`
|
|
343
|
+
})
|
|
344
|
+
], TictactoeBoardComponent);
|
|
345
|
+
|
|
346
|
+
// src/components/subway-runner/index.ts
|
|
347
|
+
import { Component as Component7, Input as Input7, ViewChild as ViewChild5, inject as inject7 } from "@angular/core";
|
|
348
|
+
var SubwayRunnerGameComponent = class {
|
|
349
|
+
constructor() {
|
|
350
|
+
this.serverUrl = "https://api.beta-gamer.com";
|
|
351
|
+
this.svc = inject7(BetaGamerService);
|
|
352
|
+
}
|
|
353
|
+
get embedUrl() {
|
|
354
|
+
return `${this.serverUrl}/embed/subway-runner`;
|
|
355
|
+
}
|
|
356
|
+
ngAfterViewInit() {
|
|
357
|
+
const iframe = this.iframeRef?.nativeElement;
|
|
358
|
+
if (!iframe) return;
|
|
359
|
+
iframe.addEventListener("load", () => {
|
|
360
|
+
iframe.contentWindow?.postMessage({ type: "bg:init", token: this.svc.token }, "*");
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
__decorateClass([
|
|
365
|
+
Input7()
|
|
366
|
+
], SubwayRunnerGameComponent.prototype, "serverUrl", 2);
|
|
367
|
+
__decorateClass([
|
|
368
|
+
ViewChild5("iframe")
|
|
369
|
+
], SubwayRunnerGameComponent.prototype, "iframeRef", 2);
|
|
370
|
+
SubwayRunnerGameComponent = __decorateClass([
|
|
371
|
+
Component7({
|
|
372
|
+
selector: "bg-subway-runner",
|
|
373
|
+
standalone: true,
|
|
374
|
+
template: `
|
|
375
|
+
@if (svc.session()?.game === 'subway-runner') {
|
|
376
|
+
<iframe #iframe [src]="embedUrl" style="border:none;width:100%;height:100%" title="Subway Runner"></iframe>
|
|
377
|
+
}
|
|
378
|
+
`
|
|
379
|
+
})
|
|
380
|
+
], SubwayRunnerGameComponent);
|
|
381
|
+
var SubwayRunnerScoreComponent = class {
|
|
382
|
+
constructor() {
|
|
383
|
+
this.svc = inject7(BetaGamerService);
|
|
384
|
+
}
|
|
385
|
+
get score() {
|
|
386
|
+
return this.svc.gameState()?.score ?? 0;
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
SubwayRunnerScoreComponent = __decorateClass([
|
|
390
|
+
Component7({
|
|
391
|
+
selector: "bg-subway-runner-score",
|
|
392
|
+
standalone: true,
|
|
393
|
+
template: `<div data-role="score"><span data-role="score-value">{{ score }}</span></div>`
|
|
394
|
+
})
|
|
395
|
+
], SubwayRunnerScoreComponent);
|
|
396
|
+
var SubwayRunnerLivesComponent = class {
|
|
397
|
+
constructor() {
|
|
398
|
+
this.svc = inject7(BetaGamerService);
|
|
399
|
+
}
|
|
400
|
+
get lives() {
|
|
401
|
+
return this.svc.gameState()?.lives ?? 0;
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
SubwayRunnerLivesComponent = __decorateClass([
|
|
405
|
+
Component7({
|
|
406
|
+
selector: "bg-subway-runner-lives",
|
|
407
|
+
standalone: true,
|
|
408
|
+
template: `<div data-role="lives"><span data-role="lives-value">{{ lives }}</span></div>`
|
|
409
|
+
})
|
|
410
|
+
], SubwayRunnerLivesComponent);
|
|
411
|
+
export {
|
|
412
|
+
BetaGamerService,
|
|
413
|
+
CheckersBoardComponent,
|
|
414
|
+
ChessBoardComponent,
|
|
415
|
+
ChessCapturedPiecesComponent,
|
|
416
|
+
ChessMoveHistoryComponent,
|
|
417
|
+
Connect4BoardComponent,
|
|
418
|
+
Connect4ScoreComponent,
|
|
419
|
+
PlayerCardComponent,
|
|
420
|
+
SubwayRunnerGameComponent,
|
|
421
|
+
SubwayRunnerLivesComponent,
|
|
422
|
+
SubwayRunnerScoreComponent,
|
|
423
|
+
TictactoeBoardComponent,
|
|
424
|
+
TimerComponent
|
|
425
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@beta-gamer/angular",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Angular SDK for Beta Gamer GaaS — composable game components",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --external @angular/core --external @angular/common --external rxjs --external socket.io-client",
|
|
20
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --external @angular/core --external @angular/common --external rxjs --external socket.io-client --watch"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@angular/common": ">=16",
|
|
24
|
+
"@angular/core": ">=16",
|
|
25
|
+
"rxjs": ">=7"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"socket.io-client": "^4.8.1"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@angular/common": "^17.0.0",
|
|
32
|
+
"@angular/core": "^17.0.0",
|
|
33
|
+
"rxjs": "^7.8.0",
|
|
34
|
+
"tsup": "^8.0.0",
|
|
35
|
+
"typescript": "^5"
|
|
36
|
+
},
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/acetennyson/beta-gamer-sdk"
|
|
40
|
+
},
|
|
41
|
+
"license": "MIT"
|
|
42
|
+
}
|