@usions/sdk 2.0.1 → 2.1.0

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.
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Usion SDK Game Socket — Socket.IO connection management
3
+ */
4
+
5
+ /**
6
+ * Add Socket.IO connection methods to game module
7
+ * @param {object} game - The game module object
8
+ * @param {object} Usion - Reference to the main Usion object
9
+ */
10
+ export function applyGameSocket(game, Usion) {
11
+ /**
12
+ * Initialize socket connection
13
+ * @private
14
+ */
15
+ game._initSocket = function(socketUrl, token, resolve, reject) {
16
+ const self = this;
17
+
18
+ // Prevent creating duplicate sockets
19
+ if (self.socket && self.socket.connected) {
20
+ self._connecting = false;
21
+ resolve();
22
+ return;
23
+ }
24
+
25
+ // Clean up any existing disconnected socket
26
+ if (self.socket) {
27
+ self.socket.disconnect();
28
+ self.socket = null;
29
+ }
30
+
31
+ try {
32
+ self.socket = io(socketUrl, {
33
+ path: '/socket.io',
34
+ transports: ['websocket', 'polling'],
35
+ auth: { token: token },
36
+ autoConnect: true,
37
+ reconnection: true,
38
+ reconnectionAttempts: 50,
39
+ reconnectionDelay: 1000,
40
+ reconnectionDelayMax: 10000
41
+ });
42
+
43
+ self.socket.on('connect', function() {
44
+ self.connected = true;
45
+ self._connecting = false;
46
+ Usion.log('Game socket connected');
47
+
48
+ // Start heartbeat to keep game session alive
49
+ if (self._heartbeatInterval) clearInterval(self._heartbeatInterval);
50
+ self._heartbeatInterval = setInterval(function() {
51
+ if (self.socket && self.connected && self.roomId) {
52
+ self.socket.emit('game:heartbeat', { room_id: self.roomId });
53
+ }
54
+ }, 25000);
55
+
56
+ // Re-join room after reconnect
57
+ if (self.roomId) {
58
+ self._joined = false;
59
+ self._joinPromise = null;
60
+ self.join(self.roomId)
61
+ .then(function() {
62
+ Usion.log('Reconnected - joined room ' + self.roomId);
63
+ self.requestSync(self._lastSequence || 0);
64
+ })
65
+ .catch(function(err) {
66
+ Usion.log('Rejoin failed: ' + (err && err.message ? err.message : String(err)));
67
+ });
68
+ }
69
+
70
+ resolve();
71
+ });
72
+
73
+ self.socket.on('connect_error', function(err) {
74
+ self._connecting = false;
75
+ Usion.log('Game socket error: ' + err.message);
76
+ if (self._eventHandlers.connectionError) {
77
+ self._eventHandlers.connectionError(err);
78
+ }
79
+ reject(err);
80
+ });
81
+
82
+ self.socket.on('disconnect', function(reason) {
83
+ self.connected = false;
84
+ self._joined = false;
85
+ self._joinPromise = null;
86
+ if (self._heartbeatInterval) {
87
+ clearInterval(self._heartbeatInterval);
88
+ self._heartbeatInterval = null;
89
+ }
90
+ Usion.log('Game socket disconnected: ' + reason);
91
+ if (self._eventHandlers.disconnect) {
92
+ self._eventHandlers.disconnect(reason);
93
+ }
94
+ });
95
+
96
+ self.socket.on('reconnect', function(attemptNumber) {
97
+ Usion.log('Game socket reconnected after ' + attemptNumber + ' attempts');
98
+ if (self._eventHandlers.reconnect) {
99
+ self._eventHandlers.reconnect(attemptNumber);
100
+ }
101
+ });
102
+
103
+ // Game event handlers
104
+ self.socket.on('game:joined', function(data) {
105
+ if (data.sequence !== undefined) {
106
+ self._lastSequence = data.sequence;
107
+ }
108
+ if (self._eventHandlers.joined) {
109
+ self._eventHandlers.joined(data);
110
+ }
111
+ });
112
+
113
+ self.socket.on('game:player_joined', function(data) {
114
+ if (self._eventHandlers.playerJoined) {
115
+ self._eventHandlers.playerJoined(data);
116
+ }
117
+ });
118
+
119
+ self.socket.on('game:player_left', function(data) {
120
+ if (self._eventHandlers.playerLeft) {
121
+ self._eventHandlers.playerLeft(data);
122
+ }
123
+ });
124
+
125
+ self.socket.on('game:state', function(data) {
126
+ if (data.sequence !== undefined) {
127
+ self._lastSequence = Math.max(self._lastSequence, data.sequence);
128
+ }
129
+ if (self._eventHandlers.stateUpdate) {
130
+ self._eventHandlers.stateUpdate(data);
131
+ }
132
+ });
133
+
134
+ self.socket.on('game:sync', function(data) {
135
+ if (data.sequence !== undefined) {
136
+ self._lastSequence = data.sequence;
137
+ }
138
+ if (self._eventHandlers.sync) {
139
+ self._eventHandlers.sync(data);
140
+ }
141
+ // Also trigger stateUpdate for backwards compat
142
+ if (self._eventHandlers.stateUpdate) {
143
+ self._eventHandlers.stateUpdate(data);
144
+ }
145
+ });
146
+
147
+ self.socket.on('game:action', function(data) {
148
+ if (data.sequence !== undefined) {
149
+ self._lastSequence = Math.max(self._lastSequence, data.sequence);
150
+ }
151
+ if (self._eventHandlers.action) {
152
+ self._eventHandlers.action(data);
153
+ }
154
+ });
155
+
156
+ self.socket.on('game:realtime', function(data) {
157
+ if (self._eventHandlers.realtime) {
158
+ self._eventHandlers.realtime(data);
159
+ }
160
+ });
161
+
162
+ self.socket.on('game:finished', function(data) {
163
+ if (data.sequence !== undefined) {
164
+ self._lastSequence = data.sequence;
165
+ }
166
+ if (self._eventHandlers.finished) {
167
+ self._eventHandlers.finished(data);
168
+ }
169
+ });
170
+
171
+ self.socket.on('game:error', function(data) {
172
+ Usion.log('Game error: ' + (data.message || data.code));
173
+ if (self._eventHandlers.error) {
174
+ self._eventHandlers.error(data);
175
+ }
176
+ });
177
+
178
+ self.socket.on('game:rematch_request', function(data) {
179
+ if (self._eventHandlers.rematchRequest) {
180
+ self._eventHandlers.rematchRequest(data);
181
+ }
182
+ });
183
+
184
+ self.socket.on('game:restarted', function(data) {
185
+ self._lastSequence = 0; // Reset sequence on rematch
186
+ if (self._eventHandlers.restarted) {
187
+ self._eventHandlers.restarted(data);
188
+ }
189
+ });
190
+
191
+ } catch (err) {
192
+ reject(err);
193
+ }
194
+ };
195
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Usion Mini App SDK v2.1
3
+ *
4
+ * JavaScript utilities for Mini Apps (Iframe Games & Services)
5
+ * Import via: <script src="https://usions.com/usion-sdk.js"></script>
6
+ *
7
+ * Features:
8
+ * - User info and authentication
9
+ * - Persistent storage (per-user, per-service)
10
+ * - Wallet/payment integration
11
+ * - Session management
12
+ * - Real-time game support via Socket.IO
13
+ */
14
+
15
+ import { core } from './core.js';
16
+ import { createUserModule } from './user.js';
17
+ import { createStorageModule } from './storage.js';
18
+ import { createWalletModule } from './wallet.js';
19
+ import { createSessionModule } from './session.js';
20
+ import { createChatModule } from './chat.js';
21
+ import { createBotModule } from './bot.js';
22
+ import { createResultsMethods } from './results.js';
23
+ import { createBackButtonMethods } from './back-button.js';
24
+ import { uiMethods } from './ui.js';
25
+ import { miscMethods } from './misc.js';
26
+ import { createFileStorageModule } from './file-storage.js';
27
+ import { createGameModule } from './game-core.js';
28
+
29
+ // Build the Usion object from core
30
+ const Usion = Object.assign({}, core);
31
+
32
+ // Attach sub-modules (these reference Usion internally)
33
+ Usion.user = createUserModule(Usion);
34
+ Usion.storage = createStorageModule(Usion);
35
+ Usion.wallet = createWalletModule(Usion);
36
+ Usion.session = createSessionModule(Usion);
37
+ Usion.chat = createChatModule(Usion);
38
+ Usion.bot = createBotModule(Usion);
39
+ Usion.fileStorage = createFileStorageModule(Usion);
40
+ Usion.game = createGameModule(Usion);
41
+
42
+ // Attach results methods directly on Usion
43
+ Object.assign(Usion, createResultsMethods(Usion));
44
+
45
+ // Attach back button methods directly on Usion
46
+ Object.assign(Usion, createBackButtonMethods(Usion));
47
+
48
+ // Attach UI utilities directly on Usion
49
+ Object.assign(Usion, uiMethods);
50
+
51
+ // Attach misc methods (submit, error, exit, share, log, on, requestPayment)
52
+ Object.assign(Usion, miscMethods);
53
+
54
+ export default Usion;
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Usion SDK Misc — submit, error, exit, share, log, on, requestPayment (legacy)
3
+ */
4
+
5
+ export const miscMethods = {
6
+ /**
7
+ * Request payment from user (legacy method)
8
+ * @deprecated Use Usion.wallet.requestPayment instead
9
+ */
10
+ requestPayment: function(amount, reason, data) {
11
+ return this.wallet.requestPayment(amount, reason, data);
12
+ },
13
+
14
+ /**
15
+ * Submit result and signal completion
16
+ * @param {object} data - Result data to send to parent
17
+ */
18
+ submit: function(data) {
19
+ this._post({
20
+ type: 'SUBMIT',
21
+ data: data
22
+ });
23
+ },
24
+
25
+ /**
26
+ * Report an error to parent app
27
+ * @param {string} message - Error message
28
+ */
29
+ error: function(message) {
30
+ this._post({
31
+ type: 'ERROR',
32
+ message: message
33
+ });
34
+ },
35
+
36
+ /**
37
+ * Request to close the mini app
38
+ * @param {object} [options] - Optional exit options
39
+ * @param {number} [options.backCount] - Number of screens to go back (default 1)
40
+ */
41
+ exit: function(options) {
42
+ var msg = { type: 'EXIT' };
43
+ if (options && typeof options.backCount === 'number' && options.backCount > 1) {
44
+ msg.backCount = options.backCount;
45
+ }
46
+ this._post(msg);
47
+ },
48
+
49
+ /**
50
+ * Share content through the app's native share and optionally post to Usions feed
51
+ *
52
+ * @param {string} contentType - Type of content: 'audio' | 'image' | 'video' | 'text' | 'mixed'
53
+ * @param {object} data - Content data to share:
54
+ * - text: Optional text/caption for the post
55
+ * - audioUrl: URL for audio content (when contentType is 'audio')
56
+ * - imageUrl: URL for image content (when contentType is 'image')
57
+ * - videoUrl: URL for video content (when contentType is 'video')
58
+ * - thumbnailUrl: Optional thumbnail URL for video/audio
59
+ * - width: Optional width for image/video
60
+ * - height: Optional height for image/video
61
+ * - duration: Optional duration in seconds for audio/video
62
+ * - media: Array of media items for 'mixed' content type
63
+ * - Each item: { type: 'image'|'video'|'audio', url: string, thumbnailUrl?, width?, height?, duration? }
64
+ */
65
+ share: function(contentType, data) {
66
+ var shareData = Object.assign({}, data, {
67
+ contentType: contentType,
68
+ serviceId: this.config.serviceId,
69
+ serviceName: this.config.serviceName
70
+ });
71
+
72
+ this._post({
73
+ type: 'SHARE',
74
+ contentType: contentType,
75
+ data: shareData
76
+ });
77
+ },
78
+
79
+ /**
80
+ * Download a file to the device's storage / gallery.
81
+ * @param {string} url - URL of the file to download
82
+ * @param {string} [filename] - Optional filename (default: 'download.mp4')
83
+ * @returns {Promise<{success: boolean, error?: string}>}
84
+ */
85
+ download: function(url, filename) {
86
+ return this._request('DOWNLOAD_FILE', {
87
+ url: url,
88
+ filename: filename || 'download.mp4'
89
+ });
90
+ },
91
+
92
+ /**
93
+ * Log message to native console (for debugging)
94
+ * @param {string} msg - Message to log
95
+ */
96
+ log: function(msg) {
97
+ this._post({
98
+ type: 'LOG',
99
+ msg: msg
100
+ });
101
+ console.log('[Usion]', msg);
102
+ },
103
+
104
+ /**
105
+ * Listen for messages from parent app
106
+ * @param {string} type - Message type to listen for
107
+ * @param {function} callback - Handler function
108
+ */
109
+ on: function(type, callback) {
110
+ window.addEventListener('message', function(event) {
111
+ let data;
112
+ try {
113
+ data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
114
+ } catch (e) {
115
+ return;
116
+ }
117
+
118
+ if (data.type === type) {
119
+ callback(data);
120
+ }
121
+ });
122
+ }
123
+ };
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Usion SDK Results — server-side result persistence across devices
3
+ */
4
+
5
+ export function createResultsMethods(Usion) {
6
+ return {
7
+ /**
8
+ * Save a result to server-side storage (persists across devices).
9
+ * @param {string} data - Result string (URL, JSON, etc.)
10
+ * @param {object} [metadata] - Optional metadata (thumbnail_url, title, type)
11
+ * @returns {Promise<object>} The saved result document
12
+ */
13
+ saveResult: function(data, metadata) {
14
+ return Usion._request('SAVE_RESULT', { data: data, metadata: metadata || {} }, 15000);
15
+ },
16
+
17
+ /**
18
+ * Delete a saved result by ID.
19
+ * @param {string} resultId - The result ID to delete
20
+ * @returns {Promise<void>}
21
+ */
22
+ deleteResult: function(resultId) {
23
+ return Usion._request('DELETE_RESULT', { resultId: resultId });
24
+ },
25
+
26
+ /**
27
+ * Get all saved results for this service (populated from INIT config).
28
+ * @returns {Array} Array of result objects
29
+ */
30
+ getResults: function() {
31
+ return Usion._results || [];
32
+ }
33
+ };
34
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Usion SDK Session Module — ephemeral session data management
3
+ */
4
+
5
+ /**
6
+ * @param {object} Usion - Reference to the main Usion object
7
+ */
8
+ export function createSessionModule(Usion) {
9
+ return {
10
+ _id: null,
11
+ _data: {},
12
+
13
+ /**
14
+ * Get the current session ID
15
+ * @returns {string|null}
16
+ */
17
+ getId: function() {
18
+ return this._id || Usion.config.sessionId || null;
19
+ },
20
+
21
+ /**
22
+ * Get session data
23
+ * @param {string} key - Optional key to get specific value
24
+ * @returns {any} Session data or specific value
25
+ */
26
+ getData: function(key) {
27
+ if (key) {
28
+ return this._data[key];
29
+ }
30
+ return this._data;
31
+ },
32
+
33
+ /**
34
+ * Set session data (ephemeral, cleared on session end)
35
+ * @param {string|object} keyOrData - Key or object of data to set
36
+ * @param {any} value - Value if key is string
37
+ */
38
+ setData: function(keyOrData, value) {
39
+ if (typeof keyOrData === 'object') {
40
+ Object.assign(this._data, keyOrData);
41
+ } else {
42
+ this._data[keyOrData] = value;
43
+ }
44
+
45
+ // Notify parent of session data change
46
+ Usion._post({
47
+ type: 'SESSION_DATA_UPDATE',
48
+ data: this._data
49
+ });
50
+ },
51
+
52
+ /**
53
+ * Clear session data
54
+ */
55
+ clear: function() {
56
+ this._data = {};
57
+ Usion._post({
58
+ type: 'SESSION_DATA_CLEAR'
59
+ });
60
+ }
61
+ };
62
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Usion SDK Storage Module — persistent storage (per-user, per-service)
3
+ */
4
+
5
+ /**
6
+ * @param {object} Usion - Reference to the main Usion object
7
+ */
8
+ export function createStorageModule(Usion) {
9
+ return {
10
+ /**
11
+ * Get a stored value
12
+ * @param {string} key - Storage key
13
+ * @returns {Promise<any>} Stored value or null
14
+ */
15
+ get: function(key) {
16
+ return Usion._request('STORAGE_GET', { key: key }).then(function(response) {
17
+ return response.value;
18
+ });
19
+ },
20
+
21
+ /**
22
+ * Set a stored value
23
+ * @param {string} key - Storage key
24
+ * @param {any} value - Value to store (will be JSON serialized)
25
+ * @returns {Promise<void>}
26
+ */
27
+ set: function(key, value) {
28
+ return Usion._request('STORAGE_SET', { key: key, value: value }).then(function() {
29
+ return;
30
+ });
31
+ },
32
+
33
+ /**
34
+ * Remove a stored value
35
+ * @param {string} key - Storage key
36
+ * @returns {Promise<void>}
37
+ */
38
+ remove: function(key) {
39
+ return Usion._request('STORAGE_REMOVE', { key: key }).then(function() {
40
+ return;
41
+ });
42
+ },
43
+
44
+ /**
45
+ * Clear all stored values for this service
46
+ * @returns {Promise<void>}
47
+ */
48
+ clear: function() {
49
+ return Usion._request('STORAGE_CLEAR', {}).then(function() {
50
+ return;
51
+ });
52
+ },
53
+
54
+ /**
55
+ * Get all keys
56
+ * @returns {Promise<string[]>}
57
+ */
58
+ keys: function() {
59
+ return Usion._request('STORAGE_KEYS', {}).then(function(response) {
60
+ return response.keys || [];
61
+ });
62
+ }
63
+ };
64
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Usion SDK UI Utilities — DOM helpers for mini apps
3
+ */
4
+
5
+ export const uiMethods = {
6
+ /**
7
+ * Set button to loading state
8
+ * @param {HTMLElement|string} btn - Button element or selector
9
+ * @param {boolean} loading - Whether to show loading state
10
+ */
11
+ setLoading: function(btn, loading) {
12
+ const el = typeof btn === 'string' ? document.querySelector(btn) : btn;
13
+ if (!el) return;
14
+
15
+ if (loading) {
16
+ el.classList.add('usion-btn-loading');
17
+ el.disabled = true;
18
+ el.dataset.originalText = el.textContent;
19
+ } else {
20
+ el.classList.remove('usion-btn-loading');
21
+ el.disabled = false;
22
+ if (el.dataset.originalText) {
23
+ el.textContent = el.dataset.originalText;
24
+ }
25
+ }
26
+ },
27
+
28
+ /**
29
+ * Show/hide an element
30
+ * @param {HTMLElement|string} el - Element or selector
31
+ * @param {boolean} show - Whether to show or hide
32
+ */
33
+ toggle: function(el, show) {
34
+ const element = typeof el === 'string' ? document.querySelector(el) : el;
35
+ if (!element) return;
36
+
37
+ if (show) {
38
+ element.classList.remove('usion-hidden', 'hidden');
39
+ element.classList.add('usion-visible');
40
+ } else {
41
+ element.classList.add('usion-hidden');
42
+ element.classList.remove('usion-visible');
43
+ }
44
+ },
45
+
46
+ /**
47
+ * Update character count display
48
+ * @param {HTMLElement|string} input - Input element or selector
49
+ * @param {HTMLElement|string} counter - Counter element or selector
50
+ * @param {number} max - Maximum characters
51
+ */
52
+ charCount: function(input, counter, max) {
53
+ const inputEl = typeof input === 'string' ? document.querySelector(input) : input;
54
+ const counterEl = typeof counter === 'string' ? document.querySelector(counter) : counter;
55
+
56
+ if (!inputEl || !counterEl) return;
57
+
58
+ function update() {
59
+ const count = inputEl.value.length;
60
+ counterEl.textContent = count + ' / ' + max;
61
+
62
+ counterEl.classList.remove('warning', 'error');
63
+ if (count > max * 0.9) {
64
+ counterEl.classList.add('error');
65
+ } else if (count > max * 0.7) {
66
+ counterEl.classList.add('warning');
67
+ }
68
+ }
69
+
70
+ inputEl.addEventListener('input', update);
71
+ update();
72
+ },
73
+
74
+ /**
75
+ * Create a selection handler for grid items
76
+ * @param {string} containerSelector - Container selector
77
+ * @param {string} itemSelector - Item selector
78
+ * @param {function} onChange - Callback when selection changes
79
+ */
80
+ selectionGrid: function(containerSelector, itemSelector, onChange) {
81
+ const container = document.querySelector(containerSelector);
82
+ if (!container) return;
83
+
84
+ let selected = null;
85
+
86
+ container.querySelectorAll(itemSelector).forEach(function(item) {
87
+ item.addEventListener('click', function() {
88
+ // Remove selection from all
89
+ container.querySelectorAll(itemSelector).forEach(function(i) {
90
+ i.classList.remove('selected');
91
+ });
92
+
93
+ // Select this one
94
+ item.classList.add('selected');
95
+ selected = item.dataset.value || item.dataset.id;
96
+
97
+ if (onChange) onChange(selected, item);
98
+ });
99
+ });
100
+
101
+ return {
102
+ getSelected: function() { return selected; },
103
+ clear: function() {
104
+ container.querySelectorAll(itemSelector).forEach(function(i) {
105
+ i.classList.remove('selected');
106
+ });
107
+ selected = null;
108
+ }
109
+ };
110
+ }
111
+ };
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Usion SDK User Module — user info and authentication
3
+ */
4
+
5
+ /**
6
+ * @param {object} Usion - Reference to the main Usion object
7
+ */
8
+ export function createUserModule(Usion) {
9
+ return {
10
+ _id: null,
11
+ _name: null,
12
+ _avatar: null,
13
+ _token: null,
14
+
15
+ /**
16
+ * Get the current user's ID
17
+ * @returns {string|null}
18
+ */
19
+ getId: function() {
20
+ return this._id || Usion.config.userId || null;
21
+ },
22
+
23
+ /**
24
+ * Get the current user's display name
25
+ * @returns {string|null}
26
+ */
27
+ getName: function() {
28
+ return this._name || Usion.config.userName || null;
29
+ },
30
+
31
+ /**
32
+ * Get the current user's avatar URL
33
+ * @returns {string|null}
34
+ */
35
+ getAvatar: function() {
36
+ return this._avatar || Usion.config.userAvatar || null;
37
+ },
38
+
39
+ /**
40
+ * Get the user's auth token for socket connections
41
+ * @returns {string|null}
42
+ */
43
+ getToken: function() {
44
+ return this._token || Usion.config.authToken || null;
45
+ },
46
+
47
+ /**
48
+ * Get full user profile
49
+ * @returns {Promise<object>} User profile with id, name, avatar
50
+ */
51
+ getProfile: function() {
52
+ return Usion._request('GET_USER_PROFILE', {}).then(function(response) {
53
+ return response.profile || {
54
+ id: Usion.user.getId(),
55
+ name: Usion.user.getName(),
56
+ avatar: Usion.user.getAvatar()
57
+ };
58
+ });
59
+ }
60
+ };
61
+ }