flux-analytics-sdk 1.0.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.
- package/package.json +20 -0
- package/src/Flux.js +467 -0
- package/src/index.js +4 -0
- package/test.html +38 -0
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "flux-analytics-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Flux Analytics SDK for JavaScript/Web",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": [
|
|
10
|
+
"flux",
|
|
11
|
+
"analytics",
|
|
12
|
+
"sdk",
|
|
13
|
+
"web"
|
|
14
|
+
],
|
|
15
|
+
"author": "Flux Team",
|
|
16
|
+
"license": "ISC",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"uuid": "^9.0.1"
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/Flux.js
ADDED
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
|
|
2
|
+
const Flux = (function () {
|
|
3
|
+
// Private state
|
|
4
|
+
let _appId = null;
|
|
5
|
+
let _firmId = null;
|
|
6
|
+
let _userId = null;
|
|
7
|
+
let _debug = false;
|
|
8
|
+
let _isProcessingQueue = false;
|
|
9
|
+
|
|
10
|
+
// Session Management
|
|
11
|
+
let _sessionId = null;
|
|
12
|
+
let _sessionStartTime = null;
|
|
13
|
+
let _initialized = false;
|
|
14
|
+
|
|
15
|
+
// Base URL for API
|
|
16
|
+
let _baseUrl = 'https://cnpawrfdekifurtkdblw.supabase.co/rest/v1';
|
|
17
|
+
let _apiKey = null;
|
|
18
|
+
|
|
19
|
+
// Constants
|
|
20
|
+
const _queueKey = 'flux_event_queue';
|
|
21
|
+
const _installKey = 'flux_app_installed';
|
|
22
|
+
const _propsKey = 'flux_user_properties';
|
|
23
|
+
const _anonIdKey = 'flux_anonymous_id';
|
|
24
|
+
const _sessionKey = 'flux_session_id'; // To persist session ID if needed
|
|
25
|
+
|
|
26
|
+
let _campaignId = null;
|
|
27
|
+
let _userProperties = {};
|
|
28
|
+
let _anonymousId = null;
|
|
29
|
+
const _screenTimers = {};
|
|
30
|
+
|
|
31
|
+
// MMP / Attribution Data
|
|
32
|
+
let _attributionData = {};
|
|
33
|
+
let _clickId = null;
|
|
34
|
+
|
|
35
|
+
// Helper: Generate UUID (Simple v4 implementation for browser if library not available)
|
|
36
|
+
function uuidv4() {
|
|
37
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
38
|
+
return crypto.randomUUID();
|
|
39
|
+
}
|
|
40
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
41
|
+
const r = (Math.random() * 16) | 0,
|
|
42
|
+
v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
43
|
+
return v.toString(16);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Helper: Get Device Info
|
|
48
|
+
function getDeviceInfo() {
|
|
49
|
+
const ua = navigator.userAgent;
|
|
50
|
+
const platform = navigator.platform || 'unknown';
|
|
51
|
+
const language = navigator.language || 'en-US';
|
|
52
|
+
const screenRes = `${window.screen.width}x${window.screen.height}`;
|
|
53
|
+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
platform: 'web',
|
|
57
|
+
os_version: platform, // Web specific
|
|
58
|
+
device_model: ua, // User Agent as model
|
|
59
|
+
language: language,
|
|
60
|
+
timezone: timeZone,
|
|
61
|
+
resolution: screenRes,
|
|
62
|
+
user_agent: ua
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Helper: LocalStorage Wrapper
|
|
67
|
+
const Storage = {
|
|
68
|
+
get: (key) => localStorage.getItem(key),
|
|
69
|
+
set: (key, value) => localStorage.setItem(key, value),
|
|
70
|
+
remove: (key) => localStorage.removeItem(key),
|
|
71
|
+
getJSON: (key) => {
|
|
72
|
+
try {
|
|
73
|
+
return JSON.parse(localStorage.getItem(key));
|
|
74
|
+
} catch (e) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
setJSON: (key, value) => localStorage.setItem(key, JSON.stringify(value))
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
// ----------------------------------------------------------------------
|
|
83
|
+
// Private Methods
|
|
84
|
+
// ----------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
async function _initAnonymousId() {
|
|
87
|
+
try {
|
|
88
|
+
// 1. Check Storage
|
|
89
|
+
_anonymousId = Storage.get(_anonIdKey);
|
|
90
|
+
|
|
91
|
+
if (!_anonymousId) {
|
|
92
|
+
// 2. Generate new if not exists
|
|
93
|
+
_anonymousId = uuidv4();
|
|
94
|
+
Storage.set(_anonIdKey, _anonymousId);
|
|
95
|
+
if (_debug) console.log('Flux: New Anonymous ID generated:', _anonymousId);
|
|
96
|
+
} else {
|
|
97
|
+
if (_debug) console.log('Flux: Existing Anonymous ID loaded:', _anonymousId);
|
|
98
|
+
}
|
|
99
|
+
} catch (e) {
|
|
100
|
+
if (_debug) console.error('Flux: Error initializing Anonymous ID:', e);
|
|
101
|
+
_anonymousId = 'unknown_device';
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function _loadUserProperties() {
|
|
106
|
+
try {
|
|
107
|
+
const props = Storage.getJSON(_propsKey);
|
|
108
|
+
if (props) {
|
|
109
|
+
_userProperties = props;
|
|
110
|
+
}
|
|
111
|
+
} catch (e) {
|
|
112
|
+
if (_debug) console.error('Flux Load Props Error:', e);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function _checkAttribution() {
|
|
117
|
+
// Web attribution logic can be implemented here
|
|
118
|
+
// Checking URL parameters for campaign_id, click_id, gclid etc.
|
|
119
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
120
|
+
|
|
121
|
+
// Example: ?campaign_id=...&click_id=...
|
|
122
|
+
const campId = urlParams.get('campaign_id') || urlParams.get('utm_campaign');
|
|
123
|
+
const clickId = urlParams.get('click_id') || urlParams.get('gclid');
|
|
124
|
+
|
|
125
|
+
if (campId) _campaignId = campId;
|
|
126
|
+
if (clickId) _clickId = clickId;
|
|
127
|
+
|
|
128
|
+
// Save if found
|
|
129
|
+
if (campId || clickId) {
|
|
130
|
+
// Logic to persist attribution if needed
|
|
131
|
+
if (_debug) console.log(`Flux: Attribution found. Campaign: ${campId}, Click: ${clickId}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function _startSession() {
|
|
136
|
+
if (_sessionId) return; // Session already active
|
|
137
|
+
|
|
138
|
+
// Check if we can recover a session from storage (optional, for page reloads)
|
|
139
|
+
// For now, let's treat every page load/init as a new session start for simplicity in Web Context
|
|
140
|
+
// OR implement session timeout logic (e.g. 30 mins inactivity)
|
|
141
|
+
|
|
142
|
+
_sessionId = uuidv4();
|
|
143
|
+
_sessionStartTime = new Date();
|
|
144
|
+
|
|
145
|
+
if (_debug) console.log('Flux: Session Started (ID: ' + _sessionId + ')');
|
|
146
|
+
|
|
147
|
+
// Sessions tablosuna oturum başlattığımızı bildir
|
|
148
|
+
await _trackSessionData({ isStart: true });
|
|
149
|
+
|
|
150
|
+
// Otomatik "session_start" olayı kaydı
|
|
151
|
+
await track('session_start');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function _trackSessionData({ isStart }) {
|
|
155
|
+
const deviceInfo = getDeviceInfo();
|
|
156
|
+
|
|
157
|
+
const sessionData = {
|
|
158
|
+
session_id: _sessionId,
|
|
159
|
+
device_id: _anonymousId,
|
|
160
|
+
user_id: _userId || _anonymousId,
|
|
161
|
+
app_id: _appId,
|
|
162
|
+
firm_id: _firmId,
|
|
163
|
+
session_start: _sessionStartTime.toISOString(),
|
|
164
|
+
platform: deviceInfo.platform,
|
|
165
|
+
is_crashed: false // Hard to detect crashes on web in same way
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
if (!isStart) {
|
|
169
|
+
const now = new Date();
|
|
170
|
+
sessionData.session_end = now.toISOString();
|
|
171
|
+
if (_sessionStartTime) {
|
|
172
|
+
sessionData.duration = Math.floor((now - _sessionStartTime) / 1000);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
await _addToQueue('sessions', sessionData);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Handle Page Visibility API for Session End/Resume?
|
|
180
|
+
// For web, "session end" is tricky (tab close). existing 'beforeunload' event is unreliable for async calls.
|
|
181
|
+
// relying on 'sendBeacon' or simple keep-alive is better.
|
|
182
|
+
// For now, we will just track session_start.
|
|
183
|
+
|
|
184
|
+
async function _checkInstall() {
|
|
185
|
+
try {
|
|
186
|
+
const isInstalled = Storage.get(_installKey);
|
|
187
|
+
|
|
188
|
+
if (!isInstalled) {
|
|
189
|
+
if (_debug) console.log('Flux: First visit (Install) detected...');
|
|
190
|
+
|
|
191
|
+
// 1. Installs tablosuna kaydet
|
|
192
|
+
await _trackInstall();
|
|
193
|
+
|
|
194
|
+
await track('app_install'); // Log as event too
|
|
195
|
+
await track('first_open');
|
|
196
|
+
|
|
197
|
+
Storage.set(_installKey, 'true');
|
|
198
|
+
if (_debug) console.log('Flux: First visit marked.');
|
|
199
|
+
}
|
|
200
|
+
} catch (e) {
|
|
201
|
+
if (_debug) console.error('Flux Install Check Error:', e);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function _trackInstall() {
|
|
206
|
+
const deviceInfo = getDeviceInfo();
|
|
207
|
+
const installData = {
|
|
208
|
+
id: uuidv4(),
|
|
209
|
+
firm_id: _firmId,
|
|
210
|
+
app_id: _appId,
|
|
211
|
+
user_id: _userId || _anonymousId,
|
|
212
|
+
device_id: _anonymousId,
|
|
213
|
+
anonymous_id: _anonymousId,
|
|
214
|
+
platform: deviceInfo.platform,
|
|
215
|
+
os_version: deviceInfo.os_version,
|
|
216
|
+
device_model: deviceInfo.device_model,
|
|
217
|
+
connection_type: navigator.connection ? navigator.connection.effectiveType : 'unknown',
|
|
218
|
+
install_date: new Date().toISOString(),
|
|
219
|
+
source: 'web_direct',
|
|
220
|
+
attribution_status: _campaignId ? 'attributed' : 'unattributed',
|
|
221
|
+
campaign_id: _campaignId
|
|
222
|
+
};
|
|
223
|
+
await _addToQueue('installs', installData);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async function _addToQueue(endpoint, data) {
|
|
227
|
+
try {
|
|
228
|
+
let currentQueue = Storage.getJSON(_queueKey) || [];
|
|
229
|
+
currentQueue.push({ endpoint, data });
|
|
230
|
+
Storage.setJSON(_queueKey, currentQueue);
|
|
231
|
+
|
|
232
|
+
// Trigger process
|
|
233
|
+
_processQueue();
|
|
234
|
+
} catch (e) {
|
|
235
|
+
if (_debug) console.error('Flux Error adding to queue:', e);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function _processQueue() {
|
|
240
|
+
if (_isProcessingQueue) return;
|
|
241
|
+
|
|
242
|
+
// Check online status
|
|
243
|
+
if (!navigator.onLine) return;
|
|
244
|
+
|
|
245
|
+
_isProcessingQueue = true;
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
// Processing loop
|
|
249
|
+
while (true) {
|
|
250
|
+
let currentQueue = Storage.getJSON(_queueKey) || [];
|
|
251
|
+
if (currentQueue.length === 0) break;
|
|
252
|
+
|
|
253
|
+
const item = currentQueue[0];
|
|
254
|
+
if (_debug) console.log(`Flux Processing Queue (${currentQueue.length} remaining)...`);
|
|
255
|
+
|
|
256
|
+
const success = await _sendToBackend(item.endpoint, item.data);
|
|
257
|
+
|
|
258
|
+
if (success) {
|
|
259
|
+
// Remove from queue
|
|
260
|
+
currentQueue = Storage.getJSON(_queueKey) || []; // Re-read to be safe
|
|
261
|
+
if (currentQueue.length > 0) { // simple check, race condition possible but low risk single-thread JS
|
|
262
|
+
currentQueue.shift();
|
|
263
|
+
Storage.setJSON(_queueKey, currentQueue);
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
if (_debug) console.log('Flux: Queue processing paused usage due to failure.');
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
} catch (e) {
|
|
271
|
+
if (_debug) console.error('Flux Queue Error:', e);
|
|
272
|
+
} finally {
|
|
273
|
+
_isProcessingQueue = false;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
async function _sendToBackend(endpoint, data) {
|
|
278
|
+
if (!_apiKey) {
|
|
279
|
+
if (_debug) console.warn('Flux Warning: API Key is missing.');
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
let url;
|
|
285
|
+
let headers = {
|
|
286
|
+
'Content-Type': 'application/json',
|
|
287
|
+
'apikey': _apiKey,
|
|
288
|
+
'Authorization': `Bearer ${_apiKey}`
|
|
289
|
+
};
|
|
290
|
+
let body = JSON.stringify(data);
|
|
291
|
+
|
|
292
|
+
// Special handling for 'events' -> RPC
|
|
293
|
+
if (endpoint === 'events') {
|
|
294
|
+
url = `${_baseUrl}/rpc/insert_event_v2`;
|
|
295
|
+
body = JSON.stringify({ event_data: data });
|
|
296
|
+
} else if (endpoint === 'sessions') {
|
|
297
|
+
url = `${_baseUrl}/sessions?on_conflict=session_id`;
|
|
298
|
+
headers['Prefer'] = 'return=representation,resolution=merge-duplicates';
|
|
299
|
+
} else if (endpoint === 'installs') {
|
|
300
|
+
url = `${_baseUrl}/installs`;
|
|
301
|
+
headers['Prefer'] = 'return=representation';
|
|
302
|
+
} else {
|
|
303
|
+
url = `${_baseUrl}/${endpoint}`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (_debug) console.log(`Flux: Posting to ${url}`, body);
|
|
307
|
+
|
|
308
|
+
const response = await fetch(url, {
|
|
309
|
+
method: 'POST',
|
|
310
|
+
headers: headers,
|
|
311
|
+
body: body
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// 2xx success, 409 conflict (duplicate) is also success
|
|
315
|
+
if (response.ok || response.status === 409) {
|
|
316
|
+
return true;
|
|
317
|
+
} else {
|
|
318
|
+
if (_debug) console.error(`Flux Error: ${response.status} - ${response.statusText}`);
|
|
319
|
+
const text = await response.text();
|
|
320
|
+
if (_debug) console.error('Response:', text);
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
} catch (e) {
|
|
325
|
+
if (_debug) console.error('Flux Network Error:', e);
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Auto tracking
|
|
331
|
+
function _setupAutoTrack() {
|
|
332
|
+
// Track visibility change to manage session
|
|
333
|
+
document.addEventListener("visibilitychange", () => {
|
|
334
|
+
if (document.visibilityState === 'visible') {
|
|
335
|
+
// Resume or start new session?
|
|
336
|
+
// For now, keep simple.
|
|
337
|
+
} else {
|
|
338
|
+
// Hidden
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
// Online/Offline listeners
|
|
343
|
+
window.addEventListener('online', _processQueue);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
// ----------------------------------------------------------------------
|
|
348
|
+
// Public API
|
|
349
|
+
// ----------------------------------------------------------------------
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Initialize Flux SDK
|
|
353
|
+
* @param {string} appId - The Application ID (UUID)
|
|
354
|
+
* @param {string} firmId - The Firm ID (UUID)
|
|
355
|
+
* @param {object} config - Optional config { apiKey, baseUrl, debug }
|
|
356
|
+
*/
|
|
357
|
+
async function init(appId, firmId, config = {}) {
|
|
358
|
+
_appId = appId;
|
|
359
|
+
_firmId = firmId;
|
|
360
|
+
_apiKey = config.apiKey || null;
|
|
361
|
+
if (config.baseUrl) _baseUrl = config.baseUrl;
|
|
362
|
+
_debug = config.debug || false;
|
|
363
|
+
|
|
364
|
+
if (_debug) console.log('Flux SDK Initializing...');
|
|
365
|
+
|
|
366
|
+
await _initAnonymousId();
|
|
367
|
+
await _checkAttribution();
|
|
368
|
+
await _loadUserProperties();
|
|
369
|
+
await _startSession();
|
|
370
|
+
await _checkInstall();
|
|
371
|
+
|
|
372
|
+
_setupAutoTrack();
|
|
373
|
+
_processQueue(); // Process any old events
|
|
374
|
+
|
|
375
|
+
_initialized = true;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Identify a user
|
|
380
|
+
* @param {string} userId
|
|
381
|
+
*/
|
|
382
|
+
function identify(userId) {
|
|
383
|
+
_userId = userId;
|
|
384
|
+
if (_debug) console.log('Flux Identified User:', _userId);
|
|
385
|
+
// Maybe trigger an identify event?
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Track an event
|
|
390
|
+
* @param {string} eventName
|
|
391
|
+
* @param {object} metadata
|
|
392
|
+
*/
|
|
393
|
+
async function track(eventName, metadata = {}) {
|
|
394
|
+
if (!_appId || !_firmId) {
|
|
395
|
+
console.error('Flux: SDK not initialized.');
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const deviceInfo = getDeviceInfo();
|
|
400
|
+
|
|
401
|
+
const payload = {
|
|
402
|
+
id: uuidv4(),
|
|
403
|
+
firm_id: _firmId,
|
|
404
|
+
app_id: _appId,
|
|
405
|
+
user_id: _userId || _anonymousId, // fallback to anon if no user
|
|
406
|
+
anonymous_id: _anonymousId,
|
|
407
|
+
campaign_id: _campaignId,
|
|
408
|
+
session_id: _sessionId,
|
|
409
|
+
device_id: _anonymousId,
|
|
410
|
+
game_type: eventName, // reusing this field for event name as per schema
|
|
411
|
+
latency_ms: 0,
|
|
412
|
+
is_error: false,
|
|
413
|
+
error_details: null,
|
|
414
|
+
metadata: {
|
|
415
|
+
..._userProperties,
|
|
416
|
+
...metadata,
|
|
417
|
+
..._attributionData,
|
|
418
|
+
platform: deviceInfo.platform,
|
|
419
|
+
os_version: deviceInfo.os_version,
|
|
420
|
+
device_model: deviceInfo.device_model, // User Agent
|
|
421
|
+
// app_version: ???
|
|
422
|
+
language: deviceInfo.language,
|
|
423
|
+
timezone: deviceInfo.timezone,
|
|
424
|
+
resolution: deviceInfo.resolution,
|
|
425
|
+
page_url: window.location.href,
|
|
426
|
+
page_title: document.title,
|
|
427
|
+
referrer: document.referrer
|
|
428
|
+
},
|
|
429
|
+
created_at: new Date().toISOString()
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
if (_debug) console.log(`Flux Tracked: ${eventName}`, payload);
|
|
433
|
+
|
|
434
|
+
await _addToQueue('events', payload);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Track a page view
|
|
439
|
+
* @param {string} name - Optional page name
|
|
440
|
+
* @param {object} metadata
|
|
441
|
+
*/
|
|
442
|
+
async function trackScreen(name, metadata = {}) {
|
|
443
|
+
const screenName = name || document.title || window.location.pathname;
|
|
444
|
+
await track('screen_view', {
|
|
445
|
+
screen_name: screenName,
|
|
446
|
+
...metadata
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
return {
|
|
452
|
+
init,
|
|
453
|
+
identify,
|
|
454
|
+
track,
|
|
455
|
+
trackScreen,
|
|
456
|
+
// Expose for debugging if needed
|
|
457
|
+
_debug: () => _debug
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
})();
|
|
461
|
+
|
|
462
|
+
// Export for module systems
|
|
463
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
464
|
+
module.exports = Flux;
|
|
465
|
+
} else if (typeof window !== 'undefined') {
|
|
466
|
+
window.Flux = Flux;
|
|
467
|
+
}
|
package/src/index.js
ADDED
package/test.html
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Flux JS SDK Test</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<h1>Flux JS SDK Test</h1>
|
|
11
|
+
<button id="trackBtn">Track Event</button>
|
|
12
|
+
<button id="identifyBtn">Identify User</button>
|
|
13
|
+
<div id="status">Converting...</div>
|
|
14
|
+
|
|
15
|
+
<script src="src/Flux.js"></script>
|
|
16
|
+
<script>
|
|
17
|
+
const APP_ID = '93a6e925-04ee-4e5a-9ae0-bc59592f87c3'; // Example App ID
|
|
18
|
+
const FIRM_ID = '93a6e925-04ee-4e5a-9ae0-bc59592f87c3'; // Example Firm ID
|
|
19
|
+
const API_KEY = 'YOUR_SUPABASE_ANON_KEY'; // Need to replace this with real key for full test
|
|
20
|
+
|
|
21
|
+
console.log('Initializing Flux...');
|
|
22
|
+
Flux.init(APP_ID, FIRM_ID, {
|
|
23
|
+
debug: true,
|
|
24
|
+
// apiKey: API_KEY // Uncomment to test with real backend
|
|
25
|
+
}).then(() => {
|
|
26
|
+
document.getElementById('status').innerText = 'Flux Initialized!';
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
document.getElementById('trackBtn').addEventListener('click', () => {
|
|
30
|
+
Flux.track('button_click', { button_id: 'trackBtn' });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
document.getElementById('identifyBtn').addEventListener('click', () => {
|
|
34
|
+
Flux.identify('user_' + Math.floor(Math.random() * 1000));
|
|
35
|
+
});
|
|
36
|
+
</script>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|