@product7/feedback-sdk 1.0.2 → 1.0.3

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.
@@ -86,64 +86,36 @@
86
86
  this.sessionExpiry = null;
87
87
  this.userContext = config.userContext || null;
88
88
 
89
- // Construct workspace-specific API URL
90
89
  if (config.apiUrl) {
91
- // If explicitly provided, use it
92
90
  this.baseURL = config.apiUrl;
93
91
  } else if (this.workspace) {
94
- // Construct from workspace: workspace.api.staging.product7.io/api/v1
95
92
  this.baseURL = `https://${this.workspace}.api.staging.product7.io/api/v1`;
96
93
  } else {
97
- // Fallback to default
98
94
  this.baseURL = 'https://api.staging.product7.io/api/v1';
99
95
  }
100
96
 
101
- console.log('[APIService] Using API URL:', this.baseURL);
102
-
103
- // Try to load existing session from localStorage
104
97
  this._loadStoredSession();
105
98
  }
106
99
 
107
100
  async init(userContext = null) {
108
- console.log('[APIService] Starting initialization...');
109
-
110
101
  if (userContext) {
111
102
  this.userContext = userContext;
112
103
  }
113
104
 
114
- // Check if we have a valid session token
115
105
  if (this.isSessionValid()) {
116
- console.log('[APIService] Found valid existing session');
117
106
  return { sessionToken: this.sessionToken };
118
107
  }
119
108
 
120
- // Try to get user context from various sources if not provided
121
- if (!this.userContext) {
122
- console.log(
123
- '[APIService] No user context provided, attempting auto-detection...'
124
- );
125
- this.userContext = this._getUserContextFromStorage();
126
- }
127
-
128
109
  if (!this.userContext || !this.workspace) {
129
110
  const error = `Missing ${!this.workspace ? 'workspace' : 'user context'} for initialization`;
130
- console.error('[APIService]', error);
131
111
  throw new APIError(400, error);
132
112
  }
133
113
 
134
- console.log('[APIService] User context detected:', this.userContext);
135
-
136
114
  const payload = {
137
115
  workspace: this.workspace,
138
116
  user: this.userContext,
139
117
  };
140
118
 
141
- console.log(
142
- '[APIService] Making init request to:',
143
- `${this.baseURL}/widget/init`
144
- );
145
- console.log('[APIService] Payload:', payload);
146
-
147
119
  try {
148
120
  const response = await this._makeRequest('/widget/init', {
149
121
  method: 'POST',
@@ -153,13 +125,8 @@
153
125
  },
154
126
  });
155
127
 
156
- console.log('[APIService] Init response:', response);
157
-
158
- // Store session token and expiry
159
128
  this.sessionToken = response.session_token;
160
129
  this.sessionExpiry = new Date(Date.now() + response.expires_in * 1000);
161
-
162
- // Store session in localStorage for persistence
163
130
  this._storeSession();
164
131
 
165
132
  return {
@@ -168,7 +135,6 @@
168
135
  expiresIn: response.expires_in,
169
136
  };
170
137
  } catch (error) {
171
- console.error('[APIService] Init failed:', error);
172
138
  throw new APIError(
173
139
  error.status || 500,
174
140
  `Failed to initialize widget: ${error.message}`,
@@ -178,7 +144,6 @@
178
144
  }
179
145
 
180
146
  async submitFeedback(feedbackData) {
181
- // Ensure we have a valid session
182
147
  if (!this.isSessionValid()) {
183
148
  await this.init();
184
149
  }
@@ -188,8 +153,7 @@
188
153
  }
189
154
 
190
155
  const payload = {
191
- board:
192
- feedbackData.board_id || feedbackData.board || feedbackData.boardId,
156
+ board: feedbackData.board_id || feedbackData.board || feedbackData.boardId,
193
157
  title: feedbackData.title,
194
158
  content: feedbackData.content,
195
159
  attachments: feedbackData.attachments || [],
@@ -207,13 +171,10 @@
207
171
 
208
172
  return response;
209
173
  } catch (error) {
210
- // If session expired, try to reinitialize once
211
174
  if (error.status === 401) {
212
175
  this.sessionToken = null;
213
176
  this.sessionExpiry = null;
214
177
  await this.init();
215
-
216
- // Retry the request with new session
217
178
  return this.submitFeedback(feedbackData);
218
179
  }
219
180
 
@@ -233,12 +194,8 @@
233
194
 
234
195
  setUserContext(userContext) {
235
196
  this.userContext = userContext;
236
- // Store in localStorage for persistence
237
197
  if (typeof localStorage !== 'undefined') {
238
- localStorage.setItem(
239
- 'feedbackSDK_userContext',
240
- JSON.stringify(userContext)
241
- );
198
+ localStorage.setItem('feedbackSDK_userContext', JSON.stringify(userContext));
242
199
  }
243
200
  }
244
201
 
@@ -251,99 +208,10 @@
251
208
  this.sessionExpiry = null;
252
209
  if (typeof localStorage !== 'undefined') {
253
210
  localStorage.removeItem('feedbackSDK_session');
211
+ localStorage.removeItem('feedbackSDK_userContext');
254
212
  }
255
213
  }
256
214
 
257
- _getUserContextFromStorage() {
258
- if (typeof localStorage === 'undefined') return null;
259
-
260
- console.log('[APIService] Attempting to detect user from storage...');
261
-
262
- try {
263
- // Try to get from feedbackSDK specific storage first
264
- const stored = localStorage.getItem('feedbackSDK_userContext');
265
- if (stored) {
266
- console.log('[APIService] Found user context in feedbackSDK storage');
267
- return JSON.parse(stored);
268
- }
269
-
270
- // Try to get from window object (if set by parent application)
271
- if (typeof window !== 'undefined' && window.FeedbackSDKUserContext) {
272
- console.log(
273
- '[APIService] Found user context in window.FeedbackSDKUserContext'
274
- );
275
- return window.FeedbackSDKUserContext;
276
- }
277
-
278
- // Check window.currentUser
279
- if (typeof window !== 'undefined' && window.currentUser) {
280
- console.log('[APIService] Found user context in window.currentUser');
281
- return this._mapUserData(window.currentUser);
282
- }
283
-
284
- // Try to extract from existing session storage
285
- const authSources = ['auth', 'currentUser', 'userSession', 'user'];
286
-
287
- for (const key of authSources) {
288
- const sessionData = localStorage.getItem(key);
289
- if (sessionData) {
290
- console.log(`[APIService] Found data in localStorage.${key}`);
291
- const parsed = JSON.parse(sessionData);
292
-
293
- // Handle nested user data (like {user: {...}, token: ...})
294
- const userData = parsed.user || parsed;
295
-
296
- if (userData && (userData.id || userData.user_id || userData.email)) {
297
- console.log('[APIService] Successfully mapped user data');
298
- return this._mapUserData(userData);
299
- }
300
- }
301
- }
302
-
303
- console.log('[APIService] No user context found in any storage location');
304
- return null;
305
- } catch (error) {
306
- console.warn(
307
- '[FeedbackSDK] Failed to load user context from storage:',
308
- error
309
- );
310
- return null;
311
- }
312
- }
313
-
314
- _mapUserData(userData) {
315
- console.log('[APIService] Mapping user data:', userData);
316
-
317
- const mapped = {
318
- user_id: userData.id || userData.user_id || userData.userId,
319
- email: userData.email,
320
- name: userData.name || userData.displayName || userData.full_name,
321
- custom_fields: {},
322
- company: {},
323
- };
324
-
325
- // Map company data if available
326
- if (userData.company || userData.organization) {
327
- const company = userData.company || userData.organization;
328
- mapped.company = {
329
- id: company.id || company.company_id,
330
- name: company.name || company.company_name,
331
- monthly_spend: company.monthly_spend || company.spend,
332
- };
333
- }
334
-
335
- // Map any additional custom fields
336
- const customFieldKeys = ['plan', 'role', 'tier', 'subscription'];
337
- customFieldKeys.forEach((key) => {
338
- if (userData[key]) {
339
- mapped.custom_fields[key] = userData[key];
340
- }
341
- });
342
-
343
- console.log('[APIService] Mapped result:', mapped);
344
- return mapped;
345
- }
346
-
347
215
  _storeSession() {
348
216
  if (typeof localStorage === 'undefined') return;
349
217
 
@@ -355,7 +223,7 @@
355
223
  };
356
224
  localStorage.setItem('feedbackSDK_session', JSON.stringify(sessionData));
357
225
  } catch (error) {
358
- console.warn('[FeedbackSDK] Failed to store session:', error);
226
+ // Silently fail if localStorage is not available
359
227
  }
360
228
  }
361
229
 
@@ -372,14 +240,12 @@
372
240
 
373
241
  return this.isSessionValid();
374
242
  } catch (error) {
375
- console.warn('[FeedbackSDK] Failed to load stored session:', error);
376
243
  return false;
377
244
  }
378
245
  }
379
246
 
380
247
  async _makeRequest(endpoint, options = {}) {
381
248
  const url = `${this.baseURL}${endpoint}`;
382
- console.log('[APIService] Making request to:', url);
383
249
 
384
250
  try {
385
251
  const response = await fetch(url, options);
@@ -390,8 +256,7 @@
390
256
 
391
257
  try {
392
258
  responseData = await response.json();
393
- errorMessage =
394
- responseData.message || responseData.error || errorMessage;
259
+ errorMessage = responseData.message || responseData.error || errorMessage;
395
260
  } catch (e) {
396
261
  errorMessage = (await response.text()) || errorMessage;
397
262
  }
@@ -409,8 +274,6 @@
409
274
  if (error instanceof APIError) {
410
275
  throw error;
411
276
  }
412
-
413
- // Network or other errors
414
277
  throw new APIError(0, error.message, null);
415
278
  }
416
279
  }
@@ -1386,7 +1249,6 @@
1386
1249
  this.widgets = new Map();
1387
1250
  this.eventBus = new EventBus();
1388
1251
 
1389
- // Initialize API service
1390
1252
  this.apiService = new APIService({
1391
1253
  apiUrl: this.config.apiUrl,
1392
1254
  workspace: this.config.workspace,
@@ -1402,10 +1264,8 @@
1402
1264
  }
1403
1265
 
1404
1266
  try {
1405
- // Initialize the API service (this will handle the /widget/init call)
1406
1267
  const initData = await this.apiService.init(this.config.userContext);
1407
1268
 
1408
- // Merge any server-provided config with local config
1409
1269
  if (initData.config) {
1410
1270
  this.config = deepMerge(this.config, initData.config);
1411
1271
  }
@@ -1430,9 +1290,7 @@
1430
1290
 
1431
1291
  createWidget(type = 'button', options = {}) {
1432
1292
  if (!this.initialized) {
1433
- throw new SDKError(
1434
- 'SDK must be initialized before creating widgets. Call init() first.'
1435
- );
1293
+ throw new SDKError('SDK must be initialized before creating widgets. Call init() first.');
1436
1294
  }
1437
1295
 
1438
1296
  const widgetId = generateId('widget');
@@ -1447,7 +1305,6 @@
1447
1305
  try {
1448
1306
  const widget = WidgetFactory.create(type, widgetOptions);
1449
1307
  this.widgets.set(widgetId, widget);
1450
-
1451
1308
  this.eventBus.emit('widget:created', { widget, type });
1452
1309
  return widget;
1453
1310
  } catch (error) {
@@ -1486,7 +1343,6 @@
1486
1343
  const oldConfig = { ...this.config };
1487
1344
  this.config = this._validateAndMergeConfig(newConfig, this.config);
1488
1345
 
1489
- // Update all existing widgets with new config
1490
1346
  for (const widget of this.widgets.values()) {
1491
1347
  widget.handleConfigUpdate(this.config);
1492
1348
  }
@@ -1506,23 +1362,17 @@
1506
1362
  }
1507
1363
 
1508
1364
  getUserContext() {
1509
- return (
1510
- this.config.userContext ||
1511
- (this.apiService ? this.apiService.getUserContext() : null)
1512
- );
1365
+ return this.config.userContext || (this.apiService ? this.apiService.getUserContext() : null);
1513
1366
  }
1514
1367
 
1515
1368
  async reinitialize(newUserContext = null) {
1516
- // Clear current session
1517
1369
  this.apiService.clearSession();
1518
1370
  this.initialized = false;
1519
1371
 
1520
- // Update user context if provided
1521
1372
  if (newUserContext) {
1522
1373
  this.setUserContext(newUserContext);
1523
1374
  }
1524
1375
 
1525
- // Reinitialize
1526
1376
  return this.init();
1527
1377
  }
1528
1378
 
@@ -1566,24 +1416,12 @@
1566
1416
  debug: false,
1567
1417
  };
1568
1418
 
1569
- const mergedConfig = deepMerge(
1570
- deepMerge(defaultConfig, existingConfig),
1571
- newConfig
1572
- );
1573
-
1574
- // Validate required config
1575
- const requiredFields = ['workspace'];
1576
- const missingFields = requiredFields.filter(
1577
- (field) => !mergedConfig[field]
1578
- );
1419
+ const mergedConfig = deepMerge(deepMerge(defaultConfig, existingConfig), newConfig);
1579
1420
 
1580
- if (missingFields.length > 0) {
1581
- throw new ConfigError(
1582
- `Missing required configuration: ${missingFields.join(', ')}`
1583
- );
1421
+ if (!mergedConfig.workspace) {
1422
+ throw new ConfigError('Missing required configuration: workspace');
1584
1423
  }
1585
1424
 
1586
- // Validate userContext structure if provided
1587
1425
  if (mergedConfig.userContext) {
1588
1426
  this._validateUserContext(mergedConfig.userContext);
1589
1427
  }
@@ -1593,12 +1431,9 @@
1593
1431
 
1594
1432
  _validateUserContext(userContext) {
1595
1433
  if (!userContext.user_id && !userContext.email) {
1596
- throw new ConfigError(
1597
- 'User context must include at least user_id or email'
1598
- );
1434
+ throw new ConfigError('User context must include at least user_id or email');
1599
1435
  }
1600
1436
 
1601
- // Validate structure matches expected API format
1602
1437
  const validStructure = {
1603
1438
  user_id: 'string',
1604
1439
  email: 'string',
@@ -1609,9 +1444,7 @@
1609
1444
 
1610
1445
  for (const [key, expectedType] of Object.entries(validStructure)) {
1611
1446
  if (userContext[key] && typeof userContext[key] !== expectedType) {
1612
- throw new ConfigError(
1613
- `User context field '${key}' must be of type '${expectedType}'`
1614
- );
1447
+ throw new ConfigError(`User context field '${key}' must be of type '${expectedType}'`);
1615
1448
  }
1616
1449
  }
1617
1450
  }
@@ -1622,7 +1455,6 @@
1622
1455
  this.updateConfig = this.updateConfig.bind(this);
1623
1456
  }
1624
1457
 
1625
- // Static helper methods
1626
1458
  static create(config) {
1627
1459
  return new FeedbackSDK(config);
1628
1460
  }
@@ -1633,9 +1465,7 @@
1633
1465
  return sdk;
1634
1466
  }
1635
1467
 
1636
- // Utility methods for external integrations
1637
1468
  static extractUserContextFromAuth(authData) {
1638
- // Helper method to extract user context from common auth structures
1639
1469
  if (!authData) return null;
1640
1470
 
1641
1471
  return {
@@ -1647,14 +1477,11 @@
1647
1477
  plan: authData.plan || authData.subscription?.plan,
1648
1478
  ...(authData.custom_fields || {}),
1649
1479
  },
1650
- company:
1651
- authData.company || authData.organization
1652
- ? {
1653
- id: authData.company?.id || authData.organization?.id,
1654
- name: authData.company?.name || authData.organization?.name,
1655
- monthly_spend: authData.company?.monthly_spend,
1656
- }
1657
- : undefined,
1480
+ company: authData.company || authData.organization ? {
1481
+ id: authData.company?.id || authData.organization?.id,
1482
+ name: authData.company?.name || authData.organization?.name,
1483
+ monthly_spend: authData.company?.monthly_spend,
1484
+ } : undefined,
1658
1485
  };
1659
1486
  }
1660
1487
  }
@@ -2218,25 +2045,11 @@
2218
2045
  `;
2219
2046
 
2220
2047
  function injectStyles() {
2221
- console.log('injectStyles called');
2222
- console.log('document exists:', typeof document !== 'undefined');
2223
- console.log('CSS_STYLES exists:', true);
2224
-
2225
- if (
2226
- typeof document !== 'undefined' &&
2227
- !document.querySelector('#feedback-sdk-styles')
2228
- ) {
2229
- console.log('Injecting CSS...');
2048
+ if (typeof document !== 'undefined' && !document.querySelector('#feedback-sdk-styles')) {
2230
2049
  const style = document.createElement('style');
2231
2050
  style.id = 'feedback-sdk-styles';
2232
2051
  style.textContent = CSS_STYLES;
2233
2052
  document.head.appendChild(style);
2234
- console.log(
2235
- 'CSS injected, style element created:',
2236
- !!document.querySelector('#feedback-sdk-styles')
2237
- );
2238
- } else {
2239
- console.log('CSS already exists or document not ready');
2240
2053
  }
2241
2054
  }
2242
2055
 
@@ -2245,11 +2058,6 @@
2245
2058
  injectStyles();
2246
2059
 
2247
2060
  const config = { ...window.FeedbackSDKConfig };
2248
-
2249
- if (!config.userContext) {
2250
- config.userContext = getUserContextFromEnvironment();
2251
- }
2252
-
2253
2061
  const sdk = new FeedbackSDK(config);
2254
2062
 
2255
2063
  sdk
@@ -2277,30 +2085,17 @@
2277
2085
 
2278
2086
  if (typeof CustomEvent !== 'undefined') {
2279
2087
  const event = new CustomEvent('FeedbackSDKReady', {
2280
- detail: {
2281
- sdk,
2282
- config: config,
2283
- initData: initData,
2284
- },
2088
+ detail: { sdk, config, initData },
2285
2089
  });
2286
2090
  window.dispatchEvent(event);
2287
2091
  }
2288
-
2289
- console.log(
2290
- '[FeedbackSDK] Successfully initialized with session:',
2291
- initData.sessionToken ? 'Yes' : 'No'
2292
- );
2293
2092
  })
2294
2093
  .catch((error) => {
2295
2094
  console.error('[FeedbackSDK] Auto-initialization failed:', error);
2296
2095
 
2297
2096
  if (typeof CustomEvent !== 'undefined') {
2298
2097
  const event = new CustomEvent('FeedbackSDKError', {
2299
- detail: {
2300
- error,
2301
- config: config,
2302
- phase: 'initialization',
2303
- },
2098
+ detail: { error, config, phase: 'initialization' },
2304
2099
  });
2305
2100
  window.dispatchEvent(event);
2306
2101
  }
@@ -2308,70 +2103,6 @@
2308
2103
  }
2309
2104
  }
2310
2105
 
2311
- function getUserContextFromEnvironment() {
2312
- if (typeof window === 'undefined') return null;
2313
-
2314
- if (window.FeedbackSDKUserContext) {
2315
- return window.FeedbackSDKUserContext;
2316
- }
2317
-
2318
- const authSources = [
2319
- () => window.auth0?.user,
2320
- () => window.firebase?.auth()?.currentUser,
2321
- () => window.amplify?.Auth?.currentAuthenticatedUser(),
2322
-
2323
- () => window.currentUser,
2324
- () => window.user,
2325
- () => window.userData,
2326
-
2327
- () => window.app?.user,
2328
- () => window.store?.getState?.()?.user,
2329
- () => window.App?.currentUser,
2330
- ];
2331
-
2332
- for (const getAuth of authSources) {
2333
- try {
2334
- const authData = getAuth();
2335
- if (authData) {
2336
- const userContext = FeedbackSDK.extractUserContextFromAuth(authData);
2337
- if (userContext && (userContext.user_id || userContext.email)) {
2338
- console.log(
2339
- '[FeedbackSDK] Auto-detected user context from',
2340
- getAuth.name || 'unknown source'
2341
- );
2342
- return userContext;
2343
- }
2344
- }
2345
- } catch (error) {
2346
- continue;
2347
- }
2348
- }
2349
-
2350
- try {
2351
- const storedAuth =
2352
- localStorage.getItem('auth') ||
2353
- localStorage.getItem('user') ||
2354
- localStorage.getItem('session');
2355
- if (storedAuth) {
2356
- const authData = JSON.parse(storedAuth);
2357
- const userContext = FeedbackSDK.extractUserContextFromAuth(authData);
2358
- if (userContext && (userContext.user_id || userContext.email)) {
2359
- console.log(
2360
- '[FeedbackSDK] Auto-detected user context from localStorage'
2361
- );
2362
- return userContext;
2363
- }
2364
- }
2365
- } catch (error) {
2366
- // Continue
2367
- }
2368
-
2369
- console.warn(
2370
- '[FeedbackSDK] No user context found. Widget initialization may require manual user context setting.'
2371
- );
2372
- return null;
2373
- }
2374
-
2375
2106
  function handleDOMReady() {
2376
2107
  if (typeof document !== 'undefined') {
2377
2108
  if (document.readyState === 'loading') {