@hyve-sdk/js 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Hyve
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,187 @@
1
+ # @hyve/sdk
2
+
3
+ TypeScript SDK for web3 authentication and game integration, providing secure signature verification and utility functions.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @hyve/sdk
9
+ # or
10
+ npm install @hyve/sdk
11
+ # or
12
+ pnpm add @hyve/sdk
13
+ ```
14
+
15
+ ## Features
16
+
17
+ - **Web3 Authentication**: EVM signature validation and verification
18
+ - **Modern & Legacy Token Support**: Dual authentication token formats
19
+ - **Security Utilities**: Domain validation and referrer checking
20
+ - **URL Parameter Parsing**: Easy extraction of authentication parameters
21
+ - **UUID Generation**: Built-in UUID v4 generation
22
+
23
+ ## Core Components
24
+
25
+ ### HyveClient
26
+ Main client class for SDK operations (currently minimal implementation).
27
+
28
+ ```typescript
29
+ import { HyveClient } from "@hyve/sdk";
30
+
31
+ const client = new HyveClient();
32
+ ```
33
+
34
+ ### Authentication Utilities
35
+
36
+ #### Parse URL Parameters
37
+ Extract authentication and game parameters from URL:
38
+
39
+ ```typescript
40
+ import { parseUrlParams } from "@hyve/sdk";
41
+
42
+ const params = parseUrlParams(window.location.search);
43
+ // Returns:
44
+ // {
45
+ // signature: string,
46
+ // message: string,
47
+ // userId: string,
48
+ // gameStartTab: string,
49
+ // hyveToken: string,
50
+ // platform: string
51
+ // }
52
+ ```
53
+
54
+ #### Verify Authentication
55
+ Unified verification supporting both modern and legacy tokens:
56
+
57
+ ```typescript
58
+ import { verifyAuthentication } from "@hyve/sdk";
59
+
60
+ const result = verifyAuthentication({
61
+ hyveToken: params.hyveToken, // Modern token format
62
+ signature: params.signature, // Legacy format
63
+ message: params.message // Legacy format
64
+ });
65
+
66
+ if (result.isValid) {
67
+ console.log('Authenticated address:', result.address);
68
+ console.log('Auth method used:', result.method); // 'modern' or 'legacy'
69
+ }
70
+ ```
71
+
72
+ #### Modern Token Verification (Hyve Token)
73
+ Verify modern authentication tokens with expiration:
74
+
75
+ ```typescript
76
+ import { verifyHyveToken } from "@hyve/sdk";
77
+
78
+ // Token format: signature.address.randomBase64.timestamp
79
+ const address = verifyHyveToken(hyveToken, 600); // 600 seconds max age
80
+ if (address) {
81
+ console.log('Valid token for address:', address);
82
+ }
83
+ ```
84
+
85
+ #### Legacy Token Verification
86
+ Verify legacy signed messages with metadata:
87
+
88
+ ```typescript
89
+ import { handleVerifyMessage, validateSignature } from "@hyve/sdk";
90
+
91
+ // Method 1: Verify message with embedded metadata
92
+ const address = handleVerifyMessage(signature, message);
93
+
94
+ // Method 2: Simple signature validation
95
+ const isValid = validateSignature(signature, message, userId);
96
+ ```
97
+
98
+ ### Security Utilities
99
+
100
+ #### Domain Validation
101
+ Check if current domain is allowed:
102
+
103
+ ```typescript
104
+ import { isDomainAllowed } from "@hyve/sdk";
105
+
106
+ // Single domain
107
+ const allowed = isDomainAllowed('example.com', window.location.hostname);
108
+
109
+ // Multiple domains with wildcard support
110
+ const allowed = isDomainAllowed(
111
+ ['example.com', '*.subdomain.com'],
112
+ window.location.hostname
113
+ );
114
+
115
+ // Note: localhost is always allowed
116
+ ```
117
+
118
+ ### Utility Functions
119
+
120
+ #### UUID Generation
121
+ Generate random UUID v4:
122
+
123
+ ```typescript
124
+ import { generateUUID } from "@hyve/sdk";
125
+
126
+ const id = generateUUID();
127
+ ```
128
+
129
+ ## Authentication Flow
130
+
131
+ ### Modern Token Flow (Recommended)
132
+ 1. User authenticates on platform
133
+ 2. Platform generates hyve-token: `signature.address.random.timestamp`
134
+ 3. Token passed via URL parameter `hyve-token`
135
+ 4. SDK verifies token with `verifyHyveToken()` or `verifyAuthentication()`
136
+
137
+ ### Legacy Token Flow
138
+ 1. User signs message containing metadata (expiration, address)
139
+ 2. Signature and message passed via URL parameters
140
+ 3. SDK verifies with `handleVerifyMessage()` or `verifyAuthentication()`
141
+
142
+ ## Security Considerations
143
+
144
+ - **Token Expiration**: Modern tokens expire after 10 minutes by default
145
+ - **Domain Validation**: Always validate origin domains in production
146
+ - **Signature Verification**: All signatures verified using ethers.js
147
+ - **Localhost Exception**: localhost always allowed for development
148
+
149
+ ## TypeScript Support
150
+
151
+ Full TypeScript support with exported types for all utilities and return values.
152
+
153
+ ## Dependencies
154
+
155
+ - **ethers**: ^6.13.7 - Ethereum signature verification
156
+ - **uuid**: (peer dependency) - UUID generation
157
+
158
+ ## Build Configuration
159
+
160
+ The SDK builds to multiple formats:
161
+ - CommonJS (`dist/index.js`)
162
+ - ES Modules (`dist/index.mjs`)
163
+ - TypeScript declarations (`dist/index.d.ts`)
164
+
165
+ ## Development
166
+
167
+ ```bash
168
+ # Install dependencies
169
+ bun install
170
+
171
+ # Type checking
172
+ bun run check-types
173
+
174
+ # Linting
175
+ bun run lint
176
+
177
+ # Build
178
+ bun run build
179
+ ```
180
+
181
+ ## Documentation
182
+
183
+ For complete documentation and examples, visit [https://docs.hyve.gg](https://docs.hyve.gg)
184
+
185
+ ## License
186
+
187
+ MIT
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Telemetry configuration options
3
+ */
4
+ interface TelemetryConfig {
5
+ /** API key for telemetry service */
6
+ apiKey?: string;
7
+ /** Environment: true for dev, false for prod. Defaults to true (dev) */
8
+ isDev?: boolean;
9
+ }
10
+ /**
11
+ * Telemetry event data structure
12
+ */
13
+ interface TelemetryEvent {
14
+ /** Unique session identifier */
15
+ session_id: string;
16
+ /** Hyve user identifier (address) */
17
+ hyve_user_id: string;
18
+ /** Location where the event occurred */
19
+ event_location: string;
20
+ /** Main category of the event */
21
+ event_category: string;
22
+ /** Primary action taken */
23
+ event_action: string;
24
+ /** Sub-category for more granular classification (null if not provided) */
25
+ event_sub_category: string | null;
26
+ /** Sub-action for detailed tracking (null if not provided) */
27
+ event_sub_action: string | null;
28
+ /** Additional data as JSON string (null if not provided) */
29
+ event_details: string | null;
30
+ }
31
+ /**
32
+ * Additional telemetry data that can be passed with events
33
+ */
34
+ interface TelemetryAdditionalData {
35
+ /** Optional additional data object (will be JSON stringified and put in event_details) */
36
+ [key: string]: any;
37
+ }
38
+
39
+ /**
40
+ * HyveClient provides telemetry and authentication functionality for Hyve games
41
+ */
42
+ declare class HyveClient {
43
+ private telemetryConfig;
44
+ private telemetryApiUrl;
45
+ private sessionId;
46
+ private userId;
47
+ /**
48
+ * Creates a new HyveClient instance
49
+ * @param config Optional telemetry configuration
50
+ */
51
+ constructor(config?: TelemetryConfig);
52
+ /**
53
+ * Authenticates a user from URL parameters
54
+ * @param urlParams URL parameters or search string
55
+ * @returns Promise resolving to boolean indicating success
56
+ */
57
+ authenticateFromUrl(urlParams?: URLSearchParams | string): Promise<boolean>;
58
+ /**
59
+ * Sends a telemetry event
60
+ * @param eventLocation Location where the event occurred
61
+ * @param eventCategory Main category of the event
62
+ * @param eventAction Primary action taken
63
+ * @param eventSubCategory Optional sub-category
64
+ * @param eventSubAction Optional sub-action
65
+ * @param eventDetails Optional event details
66
+ * @param additionalData Optional additional data
67
+ * @returns Promise resolving to boolean indicating success
68
+ */
69
+ sendTelemetry(eventLocation: string, eventCategory: string, eventAction: string, eventSubCategory?: string | null, eventSubAction?: string | null, eventDetails?: string | null, additionalData?: TelemetryAdditionalData | null): Promise<boolean>;
70
+ /**
71
+ * Updates the telemetry configuration
72
+ * @param config New telemetry configuration
73
+ */
74
+ updateTelemetryConfig(config: TelemetryConfig): void;
75
+ /**
76
+ * Gets the current user ID
77
+ * @returns Current user ID or null if not authenticated
78
+ */
79
+ getUserId(): string | null;
80
+ /**
81
+ * Gets the current session ID
82
+ * @returns Current session ID
83
+ */
84
+ getSessionId(): string;
85
+ /**
86
+ * Checks if user is authenticated
87
+ * @returns Boolean indicating authentication status
88
+ */
89
+ isUserAuthenticated(): boolean;
90
+ /**
91
+ * Logs out the current user
92
+ */
93
+ logout(): void;
94
+ /**
95
+ * Resets the client state
96
+ */
97
+ reset(): void;
98
+ }
99
+
100
+ /**
101
+ * Parses URL search parameters and returns commonly used values
102
+ * @param searchParams URLSearchParams object or string
103
+ * @returns Object containing parsed parameters with default values
104
+ */
105
+ declare function parseUrlParams(searchParams: URLSearchParams | string): {
106
+ signature: string;
107
+ message: string;
108
+ gameStartTab: string;
109
+ hyveToken: string;
110
+ platform: string;
111
+ };
112
+ /**
113
+ * Validates an EVM signature against a message and user ID
114
+ * @param signature The signature to validate
115
+ * @param message The original message that was signed
116
+ * @returns Promise resolving to boolean indicating if signature is valid
117
+ */
118
+ declare function validateSignature(signature: string, message: string): boolean;
119
+ /**
120
+ * Verifies a signed message containing metadata with expiration and address (LEGACY)
121
+ * @param signature The signature to verify
122
+ * @param message The encoded message containing metadata (JSON)
123
+ * @returns The address that signed the message if valid, false otherwise
124
+ */
125
+ declare function handleVerifyMessage(signature: string, message: string): string | false;
126
+ /**
127
+ * Verifies a modern hyve-token
128
+ * @param hyveToken The modern token in format: signature.address.randomBase64.timestamp
129
+ * @param maxAgeSec Maximum age in seconds (default: 600 = 10 minutes)
130
+ * @returns The verified address if valid, false otherwise
131
+ */
132
+ declare function verifyHyveToken(hyveToken: string, maxAgeSec?: number): string | false;
133
+ /**
134
+ * Unified authentication verification - tries modern token first, then falls back to legacy
135
+ * @param params Object containing hyveToken, signature, message from URL params
136
+ * @param maxAgeSec Maximum age for modern tokens in seconds (default: 600)
137
+ * @returns Object with verification result and method used
138
+ */
139
+ declare function verifyAuthentication(params: {
140
+ hyveToken?: string;
141
+ signature?: string;
142
+ message?: string;
143
+ }, maxAgeSec?: number): {
144
+ isValid: boolean;
145
+ address: string | null;
146
+ method: "modern" | "legacy" | "none";
147
+ error?: string;
148
+ };
149
+ /**
150
+ * Generates a random UUID (v4) using the uuid library
151
+ * @returns A string containing a randomly generated UUID
152
+ */
153
+ declare function generateUUID(): string;
154
+ /**
155
+ * Checks if the current domain is in the list of allowed domains
156
+ * @param allowedDomains Array of allowed domains or a single domain string
157
+ * @returns Boolean indicating if the current domain is allowed
158
+ */
159
+ declare function isDomainAllowed(allowedDomains?: string | string[], hostname?: string): boolean;
160
+
161
+ export { HyveClient, type TelemetryAdditionalData, type TelemetryConfig, type TelemetryEvent, generateUUID, handleVerifyMessage, isDomainAllowed, parseUrlParams, validateSignature, verifyAuthentication, verifyHyveToken };
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Telemetry configuration options
3
+ */
4
+ interface TelemetryConfig {
5
+ /** API key for telemetry service */
6
+ apiKey?: string;
7
+ /** Environment: true for dev, false for prod. Defaults to true (dev) */
8
+ isDev?: boolean;
9
+ }
10
+ /**
11
+ * Telemetry event data structure
12
+ */
13
+ interface TelemetryEvent {
14
+ /** Unique session identifier */
15
+ session_id: string;
16
+ /** Hyve user identifier (address) */
17
+ hyve_user_id: string;
18
+ /** Location where the event occurred */
19
+ event_location: string;
20
+ /** Main category of the event */
21
+ event_category: string;
22
+ /** Primary action taken */
23
+ event_action: string;
24
+ /** Sub-category for more granular classification (null if not provided) */
25
+ event_sub_category: string | null;
26
+ /** Sub-action for detailed tracking (null if not provided) */
27
+ event_sub_action: string | null;
28
+ /** Additional data as JSON string (null if not provided) */
29
+ event_details: string | null;
30
+ }
31
+ /**
32
+ * Additional telemetry data that can be passed with events
33
+ */
34
+ interface TelemetryAdditionalData {
35
+ /** Optional additional data object (will be JSON stringified and put in event_details) */
36
+ [key: string]: any;
37
+ }
38
+
39
+ /**
40
+ * HyveClient provides telemetry and authentication functionality for Hyve games
41
+ */
42
+ declare class HyveClient {
43
+ private telemetryConfig;
44
+ private telemetryApiUrl;
45
+ private sessionId;
46
+ private userId;
47
+ /**
48
+ * Creates a new HyveClient instance
49
+ * @param config Optional telemetry configuration
50
+ */
51
+ constructor(config?: TelemetryConfig);
52
+ /**
53
+ * Authenticates a user from URL parameters
54
+ * @param urlParams URL parameters or search string
55
+ * @returns Promise resolving to boolean indicating success
56
+ */
57
+ authenticateFromUrl(urlParams?: URLSearchParams | string): Promise<boolean>;
58
+ /**
59
+ * Sends a telemetry event
60
+ * @param eventLocation Location where the event occurred
61
+ * @param eventCategory Main category of the event
62
+ * @param eventAction Primary action taken
63
+ * @param eventSubCategory Optional sub-category
64
+ * @param eventSubAction Optional sub-action
65
+ * @param eventDetails Optional event details
66
+ * @param additionalData Optional additional data
67
+ * @returns Promise resolving to boolean indicating success
68
+ */
69
+ sendTelemetry(eventLocation: string, eventCategory: string, eventAction: string, eventSubCategory?: string | null, eventSubAction?: string | null, eventDetails?: string | null, additionalData?: TelemetryAdditionalData | null): Promise<boolean>;
70
+ /**
71
+ * Updates the telemetry configuration
72
+ * @param config New telemetry configuration
73
+ */
74
+ updateTelemetryConfig(config: TelemetryConfig): void;
75
+ /**
76
+ * Gets the current user ID
77
+ * @returns Current user ID or null if not authenticated
78
+ */
79
+ getUserId(): string | null;
80
+ /**
81
+ * Gets the current session ID
82
+ * @returns Current session ID
83
+ */
84
+ getSessionId(): string;
85
+ /**
86
+ * Checks if user is authenticated
87
+ * @returns Boolean indicating authentication status
88
+ */
89
+ isUserAuthenticated(): boolean;
90
+ /**
91
+ * Logs out the current user
92
+ */
93
+ logout(): void;
94
+ /**
95
+ * Resets the client state
96
+ */
97
+ reset(): void;
98
+ }
99
+
100
+ /**
101
+ * Parses URL search parameters and returns commonly used values
102
+ * @param searchParams URLSearchParams object or string
103
+ * @returns Object containing parsed parameters with default values
104
+ */
105
+ declare function parseUrlParams(searchParams: URLSearchParams | string): {
106
+ signature: string;
107
+ message: string;
108
+ gameStartTab: string;
109
+ hyveToken: string;
110
+ platform: string;
111
+ };
112
+ /**
113
+ * Validates an EVM signature against a message and user ID
114
+ * @param signature The signature to validate
115
+ * @param message The original message that was signed
116
+ * @returns Promise resolving to boolean indicating if signature is valid
117
+ */
118
+ declare function validateSignature(signature: string, message: string): boolean;
119
+ /**
120
+ * Verifies a signed message containing metadata with expiration and address (LEGACY)
121
+ * @param signature The signature to verify
122
+ * @param message The encoded message containing metadata (JSON)
123
+ * @returns The address that signed the message if valid, false otherwise
124
+ */
125
+ declare function handleVerifyMessage(signature: string, message: string): string | false;
126
+ /**
127
+ * Verifies a modern hyve-token
128
+ * @param hyveToken The modern token in format: signature.address.randomBase64.timestamp
129
+ * @param maxAgeSec Maximum age in seconds (default: 600 = 10 minutes)
130
+ * @returns The verified address if valid, false otherwise
131
+ */
132
+ declare function verifyHyveToken(hyveToken: string, maxAgeSec?: number): string | false;
133
+ /**
134
+ * Unified authentication verification - tries modern token first, then falls back to legacy
135
+ * @param params Object containing hyveToken, signature, message from URL params
136
+ * @param maxAgeSec Maximum age for modern tokens in seconds (default: 600)
137
+ * @returns Object with verification result and method used
138
+ */
139
+ declare function verifyAuthentication(params: {
140
+ hyveToken?: string;
141
+ signature?: string;
142
+ message?: string;
143
+ }, maxAgeSec?: number): {
144
+ isValid: boolean;
145
+ address: string | null;
146
+ method: "modern" | "legacy" | "none";
147
+ error?: string;
148
+ };
149
+ /**
150
+ * Generates a random UUID (v4) using the uuid library
151
+ * @returns A string containing a randomly generated UUID
152
+ */
153
+ declare function generateUUID(): string;
154
+ /**
155
+ * Checks if the current domain is in the list of allowed domains
156
+ * @param allowedDomains Array of allowed domains or a single domain string
157
+ * @returns Boolean indicating if the current domain is allowed
158
+ */
159
+ declare function isDomainAllowed(allowedDomains?: string | string[], hostname?: string): boolean;
160
+
161
+ export { HyveClient, type TelemetryAdditionalData, type TelemetryConfig, type TelemetryEvent, generateUUID, handleVerifyMessage, isDomainAllowed, parseUrlParams, validateSignature, verifyAuthentication, verifyHyveToken };
package/dist/index.js ADDED
@@ -0,0 +1,353 @@
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
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ HyveClient: () => HyveClient,
24
+ generateUUID: () => generateUUID,
25
+ handleVerifyMessage: () => handleVerifyMessage,
26
+ isDomainAllowed: () => isDomainAllowed,
27
+ parseUrlParams: () => parseUrlParams,
28
+ validateSignature: () => validateSignature,
29
+ verifyAuthentication: () => verifyAuthentication,
30
+ verifyHyveToken: () => verifyHyveToken
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ // src/utils/index.ts
35
+ var import_ethers = require("ethers");
36
+ var import_uuid = require("uuid");
37
+ function parseUrlParams(searchParams) {
38
+ const params = typeof searchParams === "string" ? new URLSearchParams(searchParams) : searchParams;
39
+ return {
40
+ signature: params.get("signature") || "",
41
+ message: params.get("message") || "",
42
+ gameStartTab: params.get("game_start_tab") || "",
43
+ hyveToken: params.get("hyve-token") || "",
44
+ platform: params.get("platform") || ""
45
+ };
46
+ }
47
+ function validateSignature(signature, message) {
48
+ try {
49
+ const recoveredAddress = import_ethers.ethers.verifyMessage(message, signature);
50
+ return !!recoveredAddress;
51
+ } catch (error) {
52
+ console.error("Signature validation error:", error);
53
+ return false;
54
+ }
55
+ }
56
+ function handleVerifyMessage(signature, message) {
57
+ try {
58
+ const metadata = JSON.parse(decodeURI(message));
59
+ if (!metadata.expiration || metadata.expiration < Date.now()) {
60
+ return false;
61
+ }
62
+ const userAddress = metadata.userId || metadata.address;
63
+ if (!userAddress) {
64
+ return false;
65
+ }
66
+ const byteMessage = new TextEncoder().encode(message);
67
+ const addressThatSignedMessage = import_ethers.ethers.verifyMessage(
68
+ byteMessage,
69
+ signature
70
+ );
71
+ if (!addressThatSignedMessage) {
72
+ return false;
73
+ }
74
+ if (addressThatSignedMessage.toLowerCase() !== userAddress.toLowerCase()) {
75
+ return false;
76
+ }
77
+ return userAddress;
78
+ } catch (error) {
79
+ console.error("Error verifying message:", error);
80
+ return false;
81
+ }
82
+ }
83
+ function verifyHyveToken(hyveToken, maxAgeSec = 600) {
84
+ try {
85
+ const parts = hyveToken.split(".");
86
+ if (parts.length !== 4) {
87
+ console.error(
88
+ "Invalid hyve-token format: expected 4 parts, got",
89
+ parts.length
90
+ );
91
+ return false;
92
+ }
93
+ const [signature, address, randomBase64, timestampStr] = parts;
94
+ if (!signature || !address || !randomBase64 || !timestampStr) {
95
+ console.error("Missing hyve-token components");
96
+ return false;
97
+ }
98
+ const message = `${address}.${randomBase64}.${timestampStr}`;
99
+ const recoveredAddress = import_ethers.ethers.verifyMessage(message, signature);
100
+ if (recoveredAddress.toLowerCase() !== address.toLowerCase()) {
101
+ console.error("Hyve-token signature verification failed");
102
+ return false;
103
+ }
104
+ const timestamp = parseInt(timestampStr, 10);
105
+ if (!Number.isFinite(timestamp)) {
106
+ console.error("Invalid hyve-token timestamp");
107
+ return false;
108
+ }
109
+ const now = Math.floor(Date.now() / 1e3);
110
+ if (now - timestamp > maxAgeSec) {
111
+ console.error(
112
+ `Hyve-token expired (age: ${now - timestamp}s, max: ${maxAgeSec}s)`
113
+ );
114
+ return false;
115
+ }
116
+ return address;
117
+ } catch (error) {
118
+ console.error("Hyve-token verification error:", error);
119
+ return false;
120
+ }
121
+ }
122
+ function verifyAuthentication(params, maxAgeSec = 600) {
123
+ if (params.hyveToken) {
124
+ const modernAddress = verifyHyveToken(params.hyveToken, maxAgeSec);
125
+ if (modernAddress) {
126
+ return {
127
+ isValid: true,
128
+ address: modernAddress,
129
+ method: "modern"
130
+ };
131
+ } else {
132
+ return {
133
+ isValid: false,
134
+ address: null,
135
+ method: "modern",
136
+ error: "Modern token verification failed"
137
+ };
138
+ }
139
+ }
140
+ if (params.signature && params.message) {
141
+ const legacyAddress = handleVerifyMessage(params.signature, params.message);
142
+ if (legacyAddress) {
143
+ return {
144
+ isValid: true,
145
+ address: legacyAddress,
146
+ method: "legacy"
147
+ };
148
+ } else {
149
+ return {
150
+ isValid: false,
151
+ address: null,
152
+ method: "legacy",
153
+ error: "Legacy token verification failed"
154
+ };
155
+ }
156
+ }
157
+ return {
158
+ isValid: false,
159
+ address: null,
160
+ method: "none",
161
+ error: "No authentication tokens provided"
162
+ };
163
+ }
164
+ function generateUUID() {
165
+ return (0, import_uuid.v4)();
166
+ }
167
+ function isDomainAllowed(allowedDomains, hostname) {
168
+ console.log("Hostname", hostname);
169
+ if (!allowedDomains) return true;
170
+ const targetHostname = hostname || "";
171
+ if (!targetHostname) return false;
172
+ if (targetHostname === "localhost" || targetHostname.startsWith("localhost:")) {
173
+ return true;
174
+ }
175
+ const domains = Array.isArray(allowedDomains) ? allowedDomains : [allowedDomains];
176
+ return domains.some((domain) => {
177
+ if (domain === targetHostname) return true;
178
+ if (domain.startsWith("*.")) {
179
+ const baseDomain = domain.substring(2);
180
+ return targetHostname.endsWith(baseDomain) && targetHostname.length > baseDomain.length && targetHostname.charAt(targetHostname.length - baseDomain.length - 1) === ".";
181
+ }
182
+ return false;
183
+ });
184
+ }
185
+
186
+ // src/core/client.ts
187
+ var HyveClient = class {
188
+ telemetryConfig;
189
+ telemetryApiUrl;
190
+ sessionId;
191
+ userId = null;
192
+ /**
193
+ * Creates a new HyveClient instance
194
+ * @param config Optional telemetry configuration
195
+ */
196
+ constructor(config) {
197
+ this.telemetryConfig = {
198
+ isDev: true,
199
+ // Default to dev environment
200
+ ...config
201
+ };
202
+ this.telemetryApiUrl = this.telemetryConfig.isDev ? "https://product-api.dev.hyve.gg/api/v1/partners/analytics/events" : "https://product-api.prod.hyve.gg/api/v1/partners/analytics/events";
203
+ this.sessionId = generateUUID();
204
+ console.log("[Hyve SDK] Client initialized with sessionId:", this.sessionId);
205
+ console.log("[Hyve SDK] Telemetry environment:", this.telemetryConfig.isDev ? "dev" : "prod");
206
+ }
207
+ /**
208
+ * Authenticates a user from URL parameters
209
+ * @param urlParams URL parameters or search string
210
+ * @returns Promise resolving to boolean indicating success
211
+ */
212
+ async authenticateFromUrl(urlParams) {
213
+ try {
214
+ const params = urlParams ? parseUrlParams(urlParams) : parseUrlParams(window.location.search);
215
+ const authResult = verifyAuthentication({
216
+ hyveToken: params.hyveToken,
217
+ signature: params.signature,
218
+ message: params.message
219
+ });
220
+ if (authResult.isValid && authResult.address) {
221
+ this.userId = authResult.address;
222
+ console.log("[Hyve SDK] Authentication successful:", authResult.address);
223
+ console.log("[Hyve SDK] Authentication method:", authResult.method);
224
+ return true;
225
+ } else {
226
+ console.error("[Hyve SDK] Authentication failed:", authResult.error);
227
+ this.userId = null;
228
+ return false;
229
+ }
230
+ } catch (error) {
231
+ console.error("[Hyve SDK] Authentication error:", error);
232
+ this.userId = null;
233
+ return false;
234
+ }
235
+ }
236
+ /**
237
+ * Sends a telemetry event
238
+ * @param eventLocation Location where the event occurred
239
+ * @param eventCategory Main category of the event
240
+ * @param eventAction Primary action taken
241
+ * @param eventSubCategory Optional sub-category
242
+ * @param eventSubAction Optional sub-action
243
+ * @param eventDetails Optional event details
244
+ * @param additionalData Optional additional data
245
+ * @returns Promise resolving to boolean indicating success
246
+ */
247
+ async sendTelemetry(eventLocation, eventCategory, eventAction, eventSubCategory, eventSubAction, eventDetails, additionalData) {
248
+ if (!this.telemetryConfig.apiKey) {
249
+ console.error("[Hyve Telemetry] API key not configured");
250
+ return false;
251
+ }
252
+ if (!this.userId) {
253
+ console.warn("[Hyve Telemetry] No user ID - sending anonymous telemetry");
254
+ }
255
+ try {
256
+ let finalEventDetails = null;
257
+ if (additionalData && Object.keys(additionalData).length > 0) {
258
+ finalEventDetails = JSON.stringify(additionalData);
259
+ } else if (eventDetails) {
260
+ finalEventDetails = eventDetails;
261
+ }
262
+ const telemetryEvent = {
263
+ session_id: this.sessionId,
264
+ hyve_user_id: this.userId || "anonymous",
265
+ event_location: eventLocation,
266
+ event_category: eventCategory,
267
+ event_action: eventAction,
268
+ event_sub_category: eventSubCategory || null,
269
+ event_sub_action: eventSubAction || null,
270
+ event_details: finalEventDetails
271
+ };
272
+ console.log("[Hyve Telemetry] Sending event:", telemetryEvent);
273
+ const response = await fetch(this.telemetryApiUrl, {
274
+ method: "POST",
275
+ headers: {
276
+ "Content-Type": "application/json",
277
+ "X-API-KEY": this.telemetryConfig.apiKey
278
+ },
279
+ body: JSON.stringify(telemetryEvent)
280
+ });
281
+ if (response.ok) {
282
+ console.log("[Hyve Telemetry] Event sent successfully:", response.status);
283
+ return true;
284
+ } else {
285
+ const errorText = await response.text();
286
+ console.error("[Hyve Telemetry] Failed to send event:", response.status, errorText);
287
+ return false;
288
+ }
289
+ } catch (error) {
290
+ console.error("[Hyve Telemetry] Error sending event:", error);
291
+ return false;
292
+ }
293
+ }
294
+ /**
295
+ * Updates the telemetry configuration
296
+ * @param config New telemetry configuration
297
+ */
298
+ updateTelemetryConfig(config) {
299
+ this.telemetryConfig = {
300
+ ...this.telemetryConfig,
301
+ ...config
302
+ };
303
+ this.telemetryApiUrl = this.telemetryConfig.isDev ? "https://product-api.dev.hyve.gg/api/v1/partners/analytics/events" : "https://product-api.prod.hyve.gg/api/v1/partners/analytics/events";
304
+ console.log("[Hyve SDK] Telemetry config updated");
305
+ }
306
+ /**
307
+ * Gets the current user ID
308
+ * @returns Current user ID or null if not authenticated
309
+ */
310
+ getUserId() {
311
+ return this.userId;
312
+ }
313
+ /**
314
+ * Gets the current session ID
315
+ * @returns Current session ID
316
+ */
317
+ getSessionId() {
318
+ return this.sessionId;
319
+ }
320
+ /**
321
+ * Checks if user is authenticated
322
+ * @returns Boolean indicating authentication status
323
+ */
324
+ isUserAuthenticated() {
325
+ return this.userId !== null;
326
+ }
327
+ /**
328
+ * Logs out the current user
329
+ */
330
+ logout() {
331
+ this.userId = null;
332
+ console.log("[Hyve SDK] User logged out");
333
+ }
334
+ /**
335
+ * Resets the client state
336
+ */
337
+ reset() {
338
+ this.logout();
339
+ this.sessionId = generateUUID();
340
+ console.log("[Hyve SDK] Client reset with new sessionId:", this.sessionId);
341
+ }
342
+ };
343
+ // Annotate the CommonJS export names for ESM import in node:
344
+ 0 && (module.exports = {
345
+ HyveClient,
346
+ generateUUID,
347
+ handleVerifyMessage,
348
+ isDomainAllowed,
349
+ parseUrlParams,
350
+ validateSignature,
351
+ verifyAuthentication,
352
+ verifyHyveToken
353
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,319 @@
1
+ // src/utils/index.ts
2
+ import { ethers } from "ethers";
3
+ import { v4 as uuidv4 } from "uuid";
4
+ function parseUrlParams(searchParams) {
5
+ const params = typeof searchParams === "string" ? new URLSearchParams(searchParams) : searchParams;
6
+ return {
7
+ signature: params.get("signature") || "",
8
+ message: params.get("message") || "",
9
+ gameStartTab: params.get("game_start_tab") || "",
10
+ hyveToken: params.get("hyve-token") || "",
11
+ platform: params.get("platform") || ""
12
+ };
13
+ }
14
+ function validateSignature(signature, message) {
15
+ try {
16
+ const recoveredAddress = ethers.verifyMessage(message, signature);
17
+ return !!recoveredAddress;
18
+ } catch (error) {
19
+ console.error("Signature validation error:", error);
20
+ return false;
21
+ }
22
+ }
23
+ function handleVerifyMessage(signature, message) {
24
+ try {
25
+ const metadata = JSON.parse(decodeURI(message));
26
+ if (!metadata.expiration || metadata.expiration < Date.now()) {
27
+ return false;
28
+ }
29
+ const userAddress = metadata.userId || metadata.address;
30
+ if (!userAddress) {
31
+ return false;
32
+ }
33
+ const byteMessage = new TextEncoder().encode(message);
34
+ const addressThatSignedMessage = ethers.verifyMessage(
35
+ byteMessage,
36
+ signature
37
+ );
38
+ if (!addressThatSignedMessage) {
39
+ return false;
40
+ }
41
+ if (addressThatSignedMessage.toLowerCase() !== userAddress.toLowerCase()) {
42
+ return false;
43
+ }
44
+ return userAddress;
45
+ } catch (error) {
46
+ console.error("Error verifying message:", error);
47
+ return false;
48
+ }
49
+ }
50
+ function verifyHyveToken(hyveToken, maxAgeSec = 600) {
51
+ try {
52
+ const parts = hyveToken.split(".");
53
+ if (parts.length !== 4) {
54
+ console.error(
55
+ "Invalid hyve-token format: expected 4 parts, got",
56
+ parts.length
57
+ );
58
+ return false;
59
+ }
60
+ const [signature, address, randomBase64, timestampStr] = parts;
61
+ if (!signature || !address || !randomBase64 || !timestampStr) {
62
+ console.error("Missing hyve-token components");
63
+ return false;
64
+ }
65
+ const message = `${address}.${randomBase64}.${timestampStr}`;
66
+ const recoveredAddress = ethers.verifyMessage(message, signature);
67
+ if (recoveredAddress.toLowerCase() !== address.toLowerCase()) {
68
+ console.error("Hyve-token signature verification failed");
69
+ return false;
70
+ }
71
+ const timestamp = parseInt(timestampStr, 10);
72
+ if (!Number.isFinite(timestamp)) {
73
+ console.error("Invalid hyve-token timestamp");
74
+ return false;
75
+ }
76
+ const now = Math.floor(Date.now() / 1e3);
77
+ if (now - timestamp > maxAgeSec) {
78
+ console.error(
79
+ `Hyve-token expired (age: ${now - timestamp}s, max: ${maxAgeSec}s)`
80
+ );
81
+ return false;
82
+ }
83
+ return address;
84
+ } catch (error) {
85
+ console.error("Hyve-token verification error:", error);
86
+ return false;
87
+ }
88
+ }
89
+ function verifyAuthentication(params, maxAgeSec = 600) {
90
+ if (params.hyveToken) {
91
+ const modernAddress = verifyHyveToken(params.hyveToken, maxAgeSec);
92
+ if (modernAddress) {
93
+ return {
94
+ isValid: true,
95
+ address: modernAddress,
96
+ method: "modern"
97
+ };
98
+ } else {
99
+ return {
100
+ isValid: false,
101
+ address: null,
102
+ method: "modern",
103
+ error: "Modern token verification failed"
104
+ };
105
+ }
106
+ }
107
+ if (params.signature && params.message) {
108
+ const legacyAddress = handleVerifyMessage(params.signature, params.message);
109
+ if (legacyAddress) {
110
+ return {
111
+ isValid: true,
112
+ address: legacyAddress,
113
+ method: "legacy"
114
+ };
115
+ } else {
116
+ return {
117
+ isValid: false,
118
+ address: null,
119
+ method: "legacy",
120
+ error: "Legacy token verification failed"
121
+ };
122
+ }
123
+ }
124
+ return {
125
+ isValid: false,
126
+ address: null,
127
+ method: "none",
128
+ error: "No authentication tokens provided"
129
+ };
130
+ }
131
+ function generateUUID() {
132
+ return uuidv4();
133
+ }
134
+ function isDomainAllowed(allowedDomains, hostname) {
135
+ console.log("Hostname", hostname);
136
+ if (!allowedDomains) return true;
137
+ const targetHostname = hostname || "";
138
+ if (!targetHostname) return false;
139
+ if (targetHostname === "localhost" || targetHostname.startsWith("localhost:")) {
140
+ return true;
141
+ }
142
+ const domains = Array.isArray(allowedDomains) ? allowedDomains : [allowedDomains];
143
+ return domains.some((domain) => {
144
+ if (domain === targetHostname) return true;
145
+ if (domain.startsWith("*.")) {
146
+ const baseDomain = domain.substring(2);
147
+ return targetHostname.endsWith(baseDomain) && targetHostname.length > baseDomain.length && targetHostname.charAt(targetHostname.length - baseDomain.length - 1) === ".";
148
+ }
149
+ return false;
150
+ });
151
+ }
152
+
153
+ // src/core/client.ts
154
+ var HyveClient = class {
155
+ telemetryConfig;
156
+ telemetryApiUrl;
157
+ sessionId;
158
+ userId = null;
159
+ /**
160
+ * Creates a new HyveClient instance
161
+ * @param config Optional telemetry configuration
162
+ */
163
+ constructor(config) {
164
+ this.telemetryConfig = {
165
+ isDev: true,
166
+ // Default to dev environment
167
+ ...config
168
+ };
169
+ this.telemetryApiUrl = this.telemetryConfig.isDev ? "https://product-api.dev.hyve.gg/api/v1/partners/analytics/events" : "https://product-api.prod.hyve.gg/api/v1/partners/analytics/events";
170
+ this.sessionId = generateUUID();
171
+ console.log("[Hyve SDK] Client initialized with sessionId:", this.sessionId);
172
+ console.log("[Hyve SDK] Telemetry environment:", this.telemetryConfig.isDev ? "dev" : "prod");
173
+ }
174
+ /**
175
+ * Authenticates a user from URL parameters
176
+ * @param urlParams URL parameters or search string
177
+ * @returns Promise resolving to boolean indicating success
178
+ */
179
+ async authenticateFromUrl(urlParams) {
180
+ try {
181
+ const params = urlParams ? parseUrlParams(urlParams) : parseUrlParams(window.location.search);
182
+ const authResult = verifyAuthentication({
183
+ hyveToken: params.hyveToken,
184
+ signature: params.signature,
185
+ message: params.message
186
+ });
187
+ if (authResult.isValid && authResult.address) {
188
+ this.userId = authResult.address;
189
+ console.log("[Hyve SDK] Authentication successful:", authResult.address);
190
+ console.log("[Hyve SDK] Authentication method:", authResult.method);
191
+ return true;
192
+ } else {
193
+ console.error("[Hyve SDK] Authentication failed:", authResult.error);
194
+ this.userId = null;
195
+ return false;
196
+ }
197
+ } catch (error) {
198
+ console.error("[Hyve SDK] Authentication error:", error);
199
+ this.userId = null;
200
+ return false;
201
+ }
202
+ }
203
+ /**
204
+ * Sends a telemetry event
205
+ * @param eventLocation Location where the event occurred
206
+ * @param eventCategory Main category of the event
207
+ * @param eventAction Primary action taken
208
+ * @param eventSubCategory Optional sub-category
209
+ * @param eventSubAction Optional sub-action
210
+ * @param eventDetails Optional event details
211
+ * @param additionalData Optional additional data
212
+ * @returns Promise resolving to boolean indicating success
213
+ */
214
+ async sendTelemetry(eventLocation, eventCategory, eventAction, eventSubCategory, eventSubAction, eventDetails, additionalData) {
215
+ if (!this.telemetryConfig.apiKey) {
216
+ console.error("[Hyve Telemetry] API key not configured");
217
+ return false;
218
+ }
219
+ if (!this.userId) {
220
+ console.warn("[Hyve Telemetry] No user ID - sending anonymous telemetry");
221
+ }
222
+ try {
223
+ let finalEventDetails = null;
224
+ if (additionalData && Object.keys(additionalData).length > 0) {
225
+ finalEventDetails = JSON.stringify(additionalData);
226
+ } else if (eventDetails) {
227
+ finalEventDetails = eventDetails;
228
+ }
229
+ const telemetryEvent = {
230
+ session_id: this.sessionId,
231
+ hyve_user_id: this.userId || "anonymous",
232
+ event_location: eventLocation,
233
+ event_category: eventCategory,
234
+ event_action: eventAction,
235
+ event_sub_category: eventSubCategory || null,
236
+ event_sub_action: eventSubAction || null,
237
+ event_details: finalEventDetails
238
+ };
239
+ console.log("[Hyve Telemetry] Sending event:", telemetryEvent);
240
+ const response = await fetch(this.telemetryApiUrl, {
241
+ method: "POST",
242
+ headers: {
243
+ "Content-Type": "application/json",
244
+ "X-API-KEY": this.telemetryConfig.apiKey
245
+ },
246
+ body: JSON.stringify(telemetryEvent)
247
+ });
248
+ if (response.ok) {
249
+ console.log("[Hyve Telemetry] Event sent successfully:", response.status);
250
+ return true;
251
+ } else {
252
+ const errorText = await response.text();
253
+ console.error("[Hyve Telemetry] Failed to send event:", response.status, errorText);
254
+ return false;
255
+ }
256
+ } catch (error) {
257
+ console.error("[Hyve Telemetry] Error sending event:", error);
258
+ return false;
259
+ }
260
+ }
261
+ /**
262
+ * Updates the telemetry configuration
263
+ * @param config New telemetry configuration
264
+ */
265
+ updateTelemetryConfig(config) {
266
+ this.telemetryConfig = {
267
+ ...this.telemetryConfig,
268
+ ...config
269
+ };
270
+ this.telemetryApiUrl = this.telemetryConfig.isDev ? "https://product-api.dev.hyve.gg/api/v1/partners/analytics/events" : "https://product-api.prod.hyve.gg/api/v1/partners/analytics/events";
271
+ console.log("[Hyve SDK] Telemetry config updated");
272
+ }
273
+ /**
274
+ * Gets the current user ID
275
+ * @returns Current user ID or null if not authenticated
276
+ */
277
+ getUserId() {
278
+ return this.userId;
279
+ }
280
+ /**
281
+ * Gets the current session ID
282
+ * @returns Current session ID
283
+ */
284
+ getSessionId() {
285
+ return this.sessionId;
286
+ }
287
+ /**
288
+ * Checks if user is authenticated
289
+ * @returns Boolean indicating authentication status
290
+ */
291
+ isUserAuthenticated() {
292
+ return this.userId !== null;
293
+ }
294
+ /**
295
+ * Logs out the current user
296
+ */
297
+ logout() {
298
+ this.userId = null;
299
+ console.log("[Hyve SDK] User logged out");
300
+ }
301
+ /**
302
+ * Resets the client state
303
+ */
304
+ reset() {
305
+ this.logout();
306
+ this.sessionId = generateUUID();
307
+ console.log("[Hyve SDK] Client reset with new sessionId:", this.sessionId);
308
+ }
309
+ };
310
+ export {
311
+ HyveClient,
312
+ generateUUID,
313
+ handleVerifyMessage,
314
+ isDomainAllowed,
315
+ parseUrlParams,
316
+ validateSignature,
317
+ verifyAuthentication,
318
+ verifyHyveToken
319
+ };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@hyve-sdk/js",
3
+ "version": "1.0.1",
4
+ "description": "Hyve SDK - TypeScript wrapper for Hyve game server integration",
5
+ "private": false,
6
+ "publishConfig": {
7
+ "access": "restricted",
8
+ "registry": "https://registry.npmjs.org/"
9
+ },
10
+ "main": "dist/index.js",
11
+ "module": "dist/index.mjs",
12
+ "types": "dist/index.d.ts",
13
+ "files": [
14
+ "dist",
15
+ "README.md",
16
+ "LICENSE"
17
+ ],
18
+ "keywords": [
19
+ "hyve",
20
+ "game",
21
+ "sdk",
22
+ "multiplayer",
23
+ "realtime",
24
+ "gaming",
25
+ "platform"
26
+ ],
27
+ "author": "Hyve",
28
+ "homepage": "https://docs.hyve.gg",
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/hyve/web-mono.git",
32
+ "directory": "packages/sdk"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/hyve/web-mono/issues"
36
+ },
37
+ "license": "MIT",
38
+ "dependencies": {
39
+ "ethers": "^6.13.7",
40
+ "uuid": "^10.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/minimatch": "^5.1.2",
44
+ "@types/uuid": "^10.0.0",
45
+ "tsup": "^8.4.0",
46
+ "typescript": "^5.3.3",
47
+ "@repo/eslint-config": "0.0.0",
48
+ "@repo/typescript-config": "0.0.0"
49
+ },
50
+ "scripts": {
51
+ "lint": "eslint . --max-warnings 0",
52
+ "check-types": "tsc --noEmit",
53
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
54
+ "publish:npm": "pnpm publish --access restricted",
55
+ "publish:dry-run": "pnpm publish --dry-run --access restricted --no-git-checks"
56
+ }
57
+ }