@grainql/analytics-web 1.6.1 → 1.7.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.
@@ -1,4 +1,4 @@
1
- /* Grain Analytics Web SDK v1.6.1 | MIT License | Development Build */
1
+ /* Grain Analytics Web SDK v1.7.0 | MIT License | Development Build */
2
2
  "use strict";
3
3
  var Grain = (() => {
4
4
  var __defProp = Object.defineProperty;
@@ -133,6 +133,113 @@ var Grain = (() => {
133
133
  console.log("[Grain Analytics]", ...args);
134
134
  }
135
135
  }
136
+ /**
137
+ * Create error digest from events
138
+ */
139
+ createErrorDigest(events) {
140
+ const eventNames = [...new Set(events.map((e) => e.eventName))];
141
+ const userIds = [...new Set(events.map((e) => e.userId))];
142
+ let totalProperties = 0;
143
+ let totalSize = 0;
144
+ events.forEach((event) => {
145
+ const properties = event.properties || {};
146
+ totalProperties += Object.keys(properties).length;
147
+ totalSize += JSON.stringify(event).length;
148
+ });
149
+ return {
150
+ eventCount: events.length,
151
+ totalProperties,
152
+ totalSize,
153
+ eventNames,
154
+ userIds
155
+ };
156
+ }
157
+ /**
158
+ * Format error with beautiful structure
159
+ */
160
+ formatError(error, context, events) {
161
+ const digest = events ? this.createErrorDigest(events) : {
162
+ eventCount: 0,
163
+ totalProperties: 0,
164
+ totalSize: 0,
165
+ eventNames: [],
166
+ userIds: []
167
+ };
168
+ let code = "UNKNOWN_ERROR";
169
+ let message = "An unknown error occurred";
170
+ if (error instanceof Error) {
171
+ message = error.message;
172
+ if (message.includes("fetch failed") || message.includes("network error")) {
173
+ code = "NETWORK_ERROR";
174
+ } else if (message.includes("timeout")) {
175
+ code = "TIMEOUT_ERROR";
176
+ } else if (message.includes("HTTP 4")) {
177
+ code = "CLIENT_ERROR";
178
+ } else if (message.includes("HTTP 5")) {
179
+ code = "SERVER_ERROR";
180
+ } else if (message.includes("JSON")) {
181
+ code = "PARSE_ERROR";
182
+ } else if (message.includes("auth") || message.includes("unauthorized")) {
183
+ code = "AUTH_ERROR";
184
+ } else if (message.includes("rate limit") || message.includes("429")) {
185
+ code = "RATE_LIMIT_ERROR";
186
+ } else {
187
+ code = "GENERAL_ERROR";
188
+ }
189
+ } else if (typeof error === "string") {
190
+ message = error;
191
+ code = "STRING_ERROR";
192
+ } else if (error && typeof error === "object" && "status" in error) {
193
+ const status = error.status;
194
+ code = `HTTP_${status}`;
195
+ message = `HTTP ${status} error`;
196
+ }
197
+ return {
198
+ code,
199
+ message,
200
+ digest,
201
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
202
+ context,
203
+ originalError: error
204
+ };
205
+ }
206
+ /**
207
+ * Log formatted error gracefully
208
+ */
209
+ logError(formattedError) {
210
+ const { code, message, digest, timestamp, context } = formattedError;
211
+ const errorOutput = {
212
+ "\u{1F6A8} Grain Analytics Error": {
213
+ "Error Code": code,
214
+ "Message": message,
215
+ "Context": context,
216
+ "Timestamp": timestamp,
217
+ "Event Digest": {
218
+ "Events": digest.eventCount,
219
+ "Properties": digest.totalProperties,
220
+ "Size (bytes)": digest.totalSize,
221
+ "Event Names": digest.eventNames.length > 0 ? digest.eventNames.join(", ") : "None",
222
+ "User IDs": digest.userIds.length > 0 ? digest.userIds.slice(0, 3).join(", ") + (digest.userIds.length > 3 ? "..." : "") : "None"
223
+ }
224
+ }
225
+ };
226
+ console.error("\u{1F6A8} Grain Analytics Error:", errorOutput);
227
+ if (this.config.debug) {
228
+ console.error(`[Grain Analytics] ${code}: ${message} (${context}) - Events: ${digest.eventCount}, Props: ${digest.totalProperties}, Size: ${digest.totalSize}B`);
229
+ }
230
+ }
231
+ /**
232
+ * Safely execute a function with error handling
233
+ */
234
+ async safeExecute(fn, context, events) {
235
+ try {
236
+ return await fn();
237
+ } catch (error) {
238
+ const formattedError = this.formatError(error, context, events);
239
+ this.logError(formattedError);
240
+ return null;
241
+ }
242
+ }
136
243
  formatEvent(event) {
137
244
  return {
138
245
  eventName: event.eventName,
@@ -216,18 +323,20 @@ var Grain = (() => {
216
323
  } catch (error) {
217
324
  lastError = error;
218
325
  if (attempt === this.config.retryAttempts) {
219
- break;
326
+ const formattedError = this.formatError(error, `sendEvents (attempt ${attempt + 1}/${this.config.retryAttempts + 1})`, events);
327
+ this.logError(formattedError);
328
+ return;
220
329
  }
221
330
  if (!this.isRetriableError(error)) {
222
- break;
331
+ const formattedError = this.formatError(error, `sendEvents (non-retriable error)`, events);
332
+ this.logError(formattedError);
333
+ return;
223
334
  }
224
335
  const delayMs = this.config.retryDelay * Math.pow(2, attempt);
225
336
  this.log(`Retrying in ${delayMs}ms after error:`, error);
226
337
  await this.delay(delayMs);
227
338
  }
228
339
  }
229
- console.error("[Grain Analytics] Failed to send events after all retries:", lastError);
230
- throw lastError;
231
340
  }
232
341
  async sendEventsWithBeacon(events) {
233
342
  if (events.length === 0)
@@ -252,7 +361,8 @@ var Grain = (() => {
252
361
  });
253
362
  this.log(`Successfully sent ${events.length} events via fetch (keepalive)`);
254
363
  } catch (error) {
255
- console.error("[Grain Analytics] Failed to send events via beacon:", error);
364
+ const formattedError = this.formatError(error, "sendEventsWithBeacon", events);
365
+ this.logError(formattedError);
256
366
  }
257
367
  }
258
368
  startFlushTimer() {
@@ -262,7 +372,8 @@ var Grain = (() => {
262
372
  this.flushTimer = window.setInterval(() => {
263
373
  if (this.eventQueue.length > 0) {
264
374
  this.flush().catch((error) => {
265
- console.error("[Grain Analytics] Auto-flush failed:", error);
375
+ const formattedError = this.formatError(error, "auto-flush");
376
+ this.logError(formattedError);
266
377
  });
267
378
  }
268
379
  }, this.config.flushInterval);
@@ -296,26 +407,34 @@ var Grain = (() => {
296
407
  });
297
408
  }
298
409
  async track(eventOrName, propertiesOrOptions, options) {
299
- if (this.isDestroyed) {
300
- throw new Error("Grain Analytics: Client has been destroyed");
301
- }
302
- let event;
303
- let opts = {};
304
- if (typeof eventOrName === "string") {
305
- event = {
306
- eventName: eventOrName,
307
- properties: propertiesOrOptions
308
- };
309
- opts = options || {};
310
- } else {
311
- event = eventOrName;
312
- opts = propertiesOrOptions || {};
313
- }
314
- const formattedEvent = this.formatEvent(event);
315
- this.eventQueue.push(formattedEvent);
316
- this.log(`Queued event: ${event.eventName}`, event.properties);
317
- if (opts.flush || this.eventQueue.length >= this.config.batchSize) {
318
- await this.flush();
410
+ try {
411
+ if (this.isDestroyed) {
412
+ const error = new Error("Grain Analytics: Client has been destroyed");
413
+ const formattedError = this.formatError(error, "track (client destroyed)");
414
+ this.logError(formattedError);
415
+ return;
416
+ }
417
+ let event;
418
+ let opts = {};
419
+ if (typeof eventOrName === "string") {
420
+ event = {
421
+ eventName: eventOrName,
422
+ properties: propertiesOrOptions
423
+ };
424
+ opts = options || {};
425
+ } else {
426
+ event = eventOrName;
427
+ opts = propertiesOrOptions || {};
428
+ }
429
+ const formattedEvent = this.formatEvent(event);
430
+ this.eventQueue.push(formattedEvent);
431
+ this.log(`Queued event: ${event.eventName}`, event.properties);
432
+ if (opts.flush || this.eventQueue.length >= this.config.batchSize) {
433
+ await this.flush();
434
+ }
435
+ } catch (error) {
436
+ const formattedError = this.formatError(error, "track");
437
+ this.logError(formattedError);
319
438
  }
320
439
  }
321
440
  /**
@@ -352,32 +471,46 @@ var Grain = (() => {
352
471
  * Set user properties
353
472
  */
354
473
  async setProperty(properties, options) {
355
- if (this.isDestroyed) {
356
- throw new Error("Grain Analytics: Client has been destroyed");
357
- }
358
- const userId = options?.userId || this.getEffectiveUserId();
359
- const propertyKeys = Object.keys(properties);
360
- if (propertyKeys.length > 4) {
361
- throw new Error("Grain Analytics: Maximum 4 properties allowed per request");
362
- }
363
- if (propertyKeys.length === 0) {
364
- throw new Error("Grain Analytics: At least one property is required");
365
- }
366
- const serializedProperties = {};
367
- for (const [key, value] of Object.entries(properties)) {
368
- if (value === null || value === void 0) {
369
- serializedProperties[key] = "";
370
- } else if (typeof value === "string") {
371
- serializedProperties[key] = value;
372
- } else {
373
- serializedProperties[key] = JSON.stringify(value);
474
+ try {
475
+ if (this.isDestroyed) {
476
+ const error = new Error("Grain Analytics: Client has been destroyed");
477
+ const formattedError = this.formatError(error, "setProperty (client destroyed)");
478
+ this.logError(formattedError);
479
+ return;
480
+ }
481
+ const userId = options?.userId || this.getEffectiveUserId();
482
+ const propertyKeys = Object.keys(properties);
483
+ if (propertyKeys.length > 4) {
484
+ const error = new Error("Grain Analytics: Maximum 4 properties allowed per request");
485
+ const formattedError = this.formatError(error, "setProperty (validation)");
486
+ this.logError(formattedError);
487
+ return;
488
+ }
489
+ if (propertyKeys.length === 0) {
490
+ const error = new Error("Grain Analytics: At least one property is required");
491
+ const formattedError = this.formatError(error, "setProperty (validation)");
492
+ this.logError(formattedError);
493
+ return;
494
+ }
495
+ const serializedProperties = {};
496
+ for (const [key, value] of Object.entries(properties)) {
497
+ if (value === null || value === void 0) {
498
+ serializedProperties[key] = "";
499
+ } else if (typeof value === "string") {
500
+ serializedProperties[key] = value;
501
+ } else {
502
+ serializedProperties[key] = JSON.stringify(value);
503
+ }
374
504
  }
505
+ const payload = {
506
+ userId,
507
+ ...serializedProperties
508
+ };
509
+ await this.sendProperties(payload);
510
+ } catch (error) {
511
+ const formattedError = this.formatError(error, "setProperty");
512
+ this.logError(formattedError);
375
513
  }
376
- const payload = {
377
- userId,
378
- ...serializedProperties
379
- };
380
- await this.sendProperties(payload);
381
514
  }
382
515
  /**
383
516
  * Send properties to the API
@@ -416,79 +549,126 @@ var Grain = (() => {
416
549
  } catch (error) {
417
550
  lastError = error;
418
551
  if (attempt === this.config.retryAttempts) {
419
- break;
552
+ const formattedError = this.formatError(error, `sendProperties (attempt ${attempt + 1}/${this.config.retryAttempts + 1})`);
553
+ this.logError(formattedError);
554
+ return;
420
555
  }
421
556
  if (!this.isRetriableError(error)) {
422
- break;
557
+ const formattedError = this.formatError(error, "sendProperties (non-retriable error)");
558
+ this.logError(formattedError);
559
+ return;
423
560
  }
424
561
  const delayMs = this.config.retryDelay * Math.pow(2, attempt);
425
562
  this.log(`Retrying in ${delayMs}ms after error:`, error);
426
563
  await this.delay(delayMs);
427
564
  }
428
565
  }
429
- console.error("[Grain Analytics] Failed to set properties after all retries:", lastError);
430
- throw lastError;
431
566
  }
432
567
  // Template event methods
433
568
  /**
434
569
  * Track user login event
435
570
  */
436
571
  async trackLogin(properties, options) {
437
- return this.track("login", properties, options);
572
+ try {
573
+ return await this.track("login", properties, options);
574
+ } catch (error) {
575
+ const formattedError = this.formatError(error, "trackLogin");
576
+ this.logError(formattedError);
577
+ }
438
578
  }
439
579
  /**
440
580
  * Track user signup event
441
581
  */
442
582
  async trackSignup(properties, options) {
443
- return this.track("signup", properties, options);
583
+ try {
584
+ return await this.track("signup", properties, options);
585
+ } catch (error) {
586
+ const formattedError = this.formatError(error, "trackSignup");
587
+ this.logError(formattedError);
588
+ }
444
589
  }
445
590
  /**
446
591
  * Track checkout event
447
592
  */
448
593
  async trackCheckout(properties, options) {
449
- return this.track("checkout", properties, options);
594
+ try {
595
+ return await this.track("checkout", properties, options);
596
+ } catch (error) {
597
+ const formattedError = this.formatError(error, "trackCheckout");
598
+ this.logError(formattedError);
599
+ }
450
600
  }
451
601
  /**
452
602
  * Track page view event
453
603
  */
454
604
  async trackPageView(properties, options) {
455
- return this.track("page_view", properties, options);
605
+ try {
606
+ return await this.track("page_view", properties, options);
607
+ } catch (error) {
608
+ const formattedError = this.formatError(error, "trackPageView");
609
+ this.logError(formattedError);
610
+ }
456
611
  }
457
612
  /**
458
613
  * Track purchase event
459
614
  */
460
615
  async trackPurchase(properties, options) {
461
- return this.track("purchase", properties, options);
616
+ try {
617
+ return await this.track("purchase", properties, options);
618
+ } catch (error) {
619
+ const formattedError = this.formatError(error, "trackPurchase");
620
+ this.logError(formattedError);
621
+ }
462
622
  }
463
623
  /**
464
624
  * Track search event
465
625
  */
466
626
  async trackSearch(properties, options) {
467
- return this.track("search", properties, options);
627
+ try {
628
+ return await this.track("search", properties, options);
629
+ } catch (error) {
630
+ const formattedError = this.formatError(error, "trackSearch");
631
+ this.logError(formattedError);
632
+ }
468
633
  }
469
634
  /**
470
635
  * Track add to cart event
471
636
  */
472
637
  async trackAddToCart(properties, options) {
473
- return this.track("add_to_cart", properties, options);
638
+ try {
639
+ return await this.track("add_to_cart", properties, options);
640
+ } catch (error) {
641
+ const formattedError = this.formatError(error, "trackAddToCart");
642
+ this.logError(formattedError);
643
+ }
474
644
  }
475
645
  /**
476
646
  * Track remove from cart event
477
647
  */
478
648
  async trackRemoveFromCart(properties, options) {
479
- return this.track("remove_from_cart", properties, options);
649
+ try {
650
+ return await this.track("remove_from_cart", properties, options);
651
+ } catch (error) {
652
+ const formattedError = this.formatError(error, "trackRemoveFromCart");
653
+ this.logError(formattedError);
654
+ }
480
655
  }
481
656
  /**
482
657
  * Manually flush all queued events
483
658
  */
484
659
  async flush() {
485
- if (this.eventQueue.length === 0)
486
- return;
487
- const eventsToSend = [...this.eventQueue];
488
- this.eventQueue = [];
489
- const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
490
- for (const chunk of chunks) {
491
- await this.sendEvents(chunk);
660
+ try {
661
+ if (this.eventQueue.length === 0)
662
+ return;
663
+ const eventsToSend = [...this.eventQueue];
664
+ this.eventQueue = [];
665
+ const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
666
+ for (const chunk of chunks) {
667
+ await this.sendEvents(chunk);
668
+ }
669
+ } catch (error) {
670
+ const formattedError = this.formatError(error, "flush");
671
+ this.logError(formattedError);
492
672
  }
493
673
  }
494
674
  // Remote Config Methods
@@ -547,82 +727,98 @@ var Grain = (() => {
547
727
  * Fetch configurations from API
548
728
  */
549
729
  async fetchConfig(options = {}) {
550
- if (this.isDestroyed) {
551
- throw new Error("Grain Analytics: Client has been destroyed");
552
- }
553
- const userId = options.userId || this.getEffectiveUserId();
554
- const immediateKeys = options.immediateKeys || [];
555
- const properties = options.properties || {};
556
- const request = {
557
- userId,
558
- immediateKeys,
559
- properties
560
- };
561
- let lastError;
562
- for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {
563
- try {
564
- const headers = await this.getAuthHeaders();
565
- const url = `${this.config.apiUrl}/v1/client/${encodeURIComponent(this.config.tenantId)}/config/configurations`;
566
- this.log(`Fetching configurations for user ${userId} (attempt ${attempt + 1})`);
567
- const response = await fetch(url, {
568
- method: "POST",
569
- headers,
570
- body: JSON.stringify(request)
571
- });
572
- if (!response.ok) {
573
- let errorMessage = `HTTP ${response.status}`;
574
- try {
575
- const errorBody = await response.json();
576
- if (errorBody?.message) {
577
- errorMessage = errorBody.message;
578
- }
579
- } catch {
580
- const errorText = await response.text();
581
- if (errorText) {
582
- errorMessage = errorText;
730
+ try {
731
+ if (this.isDestroyed) {
732
+ const error = new Error("Grain Analytics: Client has been destroyed");
733
+ const formattedError = this.formatError(error, "fetchConfig (client destroyed)");
734
+ this.logError(formattedError);
735
+ return null;
736
+ }
737
+ const userId = options.userId || this.getEffectiveUserId();
738
+ const immediateKeys = options.immediateKeys || [];
739
+ const properties = options.properties || {};
740
+ const request = {
741
+ userId,
742
+ immediateKeys,
743
+ properties
744
+ };
745
+ let lastError;
746
+ for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {
747
+ try {
748
+ const headers = await this.getAuthHeaders();
749
+ const url = `${this.config.apiUrl}/v1/client/${encodeURIComponent(this.config.tenantId)}/config/configurations`;
750
+ this.log(`Fetching configurations for user ${userId} (attempt ${attempt + 1})`);
751
+ const response = await fetch(url, {
752
+ method: "POST",
753
+ headers,
754
+ body: JSON.stringify(request)
755
+ });
756
+ if (!response.ok) {
757
+ let errorMessage = `HTTP ${response.status}`;
758
+ try {
759
+ const errorBody = await response.json();
760
+ if (errorBody?.message) {
761
+ errorMessage = errorBody.message;
762
+ }
763
+ } catch {
764
+ const errorText = await response.text();
765
+ if (errorText) {
766
+ errorMessage = errorText;
767
+ }
583
768
  }
769
+ const error = new Error(`Failed to fetch configurations: ${errorMessage}`);
770
+ error.status = response.status;
771
+ throw error;
584
772
  }
585
- const error = new Error(`Failed to fetch configurations: ${errorMessage}`);
586
- error.status = response.status;
587
- throw error;
588
- }
589
- const configResponse = await response.json();
590
- if (configResponse.configurations) {
591
- this.updateConfigCache(configResponse, userId);
592
- }
593
- this.log(`Successfully fetched configurations for user ${userId}:`, configResponse);
594
- return configResponse;
595
- } catch (error) {
596
- lastError = error;
597
- if (attempt === this.config.retryAttempts) {
598
- break;
599
- }
600
- if (!this.isRetriableError(error)) {
601
- break;
773
+ const configResponse = await response.json();
774
+ if (configResponse.configurations) {
775
+ this.updateConfigCache(configResponse, userId);
776
+ }
777
+ this.log(`Successfully fetched configurations for user ${userId}:`, configResponse);
778
+ return configResponse;
779
+ } catch (error) {
780
+ lastError = error;
781
+ if (attempt === this.config.retryAttempts) {
782
+ const formattedError = this.formatError(error, `fetchConfig (attempt ${attempt + 1}/${this.config.retryAttempts + 1})`);
783
+ this.logError(formattedError);
784
+ return null;
785
+ }
786
+ if (!this.isRetriableError(error)) {
787
+ const formattedError = this.formatError(error, "fetchConfig (non-retriable error)");
788
+ this.logError(formattedError);
789
+ return null;
790
+ }
791
+ const delayMs = this.config.retryDelay * Math.pow(2, attempt);
792
+ this.log(`Retrying config fetch in ${delayMs}ms after error:`, error);
793
+ await this.delay(delayMs);
602
794
  }
603
- const delayMs = this.config.retryDelay * Math.pow(2, attempt);
604
- this.log(`Retrying config fetch in ${delayMs}ms after error:`, error);
605
- await this.delay(delayMs);
606
795
  }
796
+ return null;
797
+ } catch (error) {
798
+ const formattedError = this.formatError(error, "fetchConfig");
799
+ this.logError(formattedError);
800
+ return null;
607
801
  }
608
- console.error("[Grain Analytics] Failed to fetch configurations after all retries:", lastError);
609
- throw lastError;
610
802
  }
611
803
  /**
612
804
  * Get configuration asynchronously (cache-first with fallback to API)
613
805
  */
614
806
  async getConfigAsync(key, options = {}) {
615
- if (!options.forceRefresh && this.configCache?.configurations?.[key]) {
616
- return this.configCache.configurations[key];
617
- }
618
- if (!options.forceRefresh && this.config.defaultConfigurations?.[key]) {
619
- return this.config.defaultConfigurations[key];
620
- }
621
807
  try {
808
+ if (!options.forceRefresh && this.configCache?.configurations?.[key]) {
809
+ return this.configCache.configurations[key];
810
+ }
811
+ if (!options.forceRefresh && this.config.defaultConfigurations?.[key]) {
812
+ return this.config.defaultConfigurations[key];
813
+ }
622
814
  const response = await this.fetchConfig(options);
623
- return response.configurations[key];
815
+ if (response) {
816
+ return response.configurations[key];
817
+ }
818
+ return this.config.defaultConfigurations?.[key];
624
819
  } catch (error) {
625
- this.log(`Failed to fetch config for key "${key}":`, error);
820
+ const formattedError = this.formatError(error, "getConfigAsync");
821
+ this.logError(formattedError);
626
822
  return this.config.defaultConfigurations?.[key];
627
823
  }
628
824
  }
@@ -630,14 +826,18 @@ var Grain = (() => {
630
826
  * Get all configurations asynchronously (cache-first with fallback to API)
631
827
  */
632
828
  async getAllConfigsAsync(options = {}) {
633
- if (!options.forceRefresh && this.configCache?.configurations) {
634
- return { ...this.config.defaultConfigurations, ...this.configCache.configurations };
635
- }
636
829
  try {
830
+ if (!options.forceRefresh && this.configCache?.configurations) {
831
+ return { ...this.config.defaultConfigurations, ...this.configCache.configurations };
832
+ }
637
833
  const response = await this.fetchConfig(options);
638
- return { ...this.config.defaultConfigurations, ...response.configurations };
834
+ if (response) {
835
+ return { ...this.config.defaultConfigurations, ...response.configurations };
836
+ }
837
+ return { ...this.config.defaultConfigurations };
639
838
  } catch (error) {
640
- this.log("Failed to fetch all configs:", error);
839
+ const formattedError = this.formatError(error, "getAllConfigsAsync");
840
+ this.logError(formattedError);
641
841
  return { ...this.config.defaultConfigurations };
642
842
  }
643
843
  }
@@ -695,7 +895,8 @@ var Grain = (() => {
695
895
  this.configRefreshTimer = window.setInterval(() => {
696
896
  if (!this.isDestroyed && this.globalUserId) {
697
897
  this.fetchConfig().catch((error) => {
698
- console.error("[Grain Analytics] Auto-config refresh failed:", error);
898
+ const formattedError = this.formatError(error, "auto-config refresh");
899
+ this.logError(formattedError);
699
900
  });
700
901
  }
701
902
  }, this.config.configRefreshInterval);
@@ -713,15 +914,18 @@ var Grain = (() => {
713
914
  * Preload configurations for immediate access
714
915
  */
715
916
  async preloadConfig(immediateKeys = [], properties) {
716
- if (!this.globalUserId) {
717
- this.log("Cannot preload config: no user ID set");
718
- return;
719
- }
720
917
  try {
721
- await this.fetchConfig({ immediateKeys, properties });
722
- this.startConfigRefreshTimer();
918
+ if (!this.globalUserId) {
919
+ this.log("Cannot preload config: no user ID set");
920
+ return;
921
+ }
922
+ const response = await this.fetchConfig({ immediateKeys, properties });
923
+ if (response) {
924
+ this.startConfigRefreshTimer();
925
+ }
723
926
  } catch (error) {
724
- this.log("Failed to preload config:", error);
927
+ const formattedError = this.formatError(error, "preloadConfig");
928
+ this.logError(formattedError);
725
929
  }
726
930
  }
727
931
  /**