@firebase/app 0.7.17-canary.2b98b28f2 → 0.7.17-canary.927c1afc1

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.
@@ -16,7 +16,7 @@
16
16
  */
17
17
  import { ComponentContainer } from '@firebase/component';
18
18
  import { FirebaseApp } from './public-types';
19
- import { HeartbeatsByUserAgent, HeartbeatService, HeartbeatStorage, SingleDateHeartbeat } from './types';
19
+ import { HeartbeatsByUserAgent, HeartbeatService, HeartbeatsInIndexedDB, HeartbeatStorage, SingleDateHeartbeat } from './types';
20
20
  export declare class HeartbeatServiceImpl implements HeartbeatService {
21
21
  private readonly container;
22
22
  /**
@@ -33,14 +33,14 @@ export declare class HeartbeatServiceImpl implements HeartbeatService {
33
33
  * be kept in sync with indexedDB.
34
34
  * Leave public for easier testing.
35
35
  */
36
- _heartbeatsCache: SingleDateHeartbeat[] | null;
36
+ _heartbeatsCache: HeartbeatsInIndexedDB | null;
37
37
  /**
38
38
  * the initialization promise for populating heartbeatCache.
39
39
  * If getHeartbeatsHeader() is called before the promise resolves
40
40
  * (hearbeatsCache == null), it should wait for this promise
41
41
  * Leave public for easier testing.
42
42
  */
43
- _heartbeatsCachePromise: Promise<SingleDateHeartbeat[]>;
43
+ _heartbeatsCachePromise: Promise<HeartbeatsInIndexedDB>;
44
44
  constructor(container: ComponentContainer);
45
45
  /**
46
46
  * Called to report a heartbeat. The function will generate
@@ -54,7 +54,8 @@ export declare class HeartbeatServiceImpl implements HeartbeatService {
54
54
  * Returns a base64 encoded string which can be attached to the heartbeat-specific header directly.
55
55
  * It also clears all heartbeats from memory as well as in IndexedDB.
56
56
  *
57
- * NOTE: It will read heartbeats from the heartbeatsCache, instead of from indexedDB to reduce latency
57
+ * NOTE: Consuming product SDKs should not send the header if this method
58
+ * returns an empty string.
58
59
  */
59
60
  getHeartbeatsHeader(): Promise<string>;
60
61
  }
@@ -70,11 +71,9 @@ export declare class HeartbeatStorageImpl implements HeartbeatStorage {
70
71
  /**
71
72
  * Read all heartbeats.
72
73
  */
73
- read(): Promise<SingleDateHeartbeat[]>;
74
- overwrite(heartbeats: SingleDateHeartbeat[]): Promise<void>;
75
- add(heartbeats: SingleDateHeartbeat[]): Promise<void>;
76
- delete(heartbeats: SingleDateHeartbeat[]): Promise<void>;
77
- deleteAll(): Promise<void>;
74
+ read(): Promise<HeartbeatsInIndexedDB>;
75
+ overwrite(heartbeatsObject: HeartbeatsInIndexedDB): Promise<void>;
76
+ add(heartbeatsObject: HeartbeatsInIndexedDB): Promise<void>;
78
77
  }
79
78
  /**
80
79
  * Calculate bytes of a HeartbeatsByUserAgent array after being wrapped
@@ -18,4 +18,3 @@ import { FirebaseApp } from './public-types';
18
18
  import { HeartbeatsInIndexedDB } from './types';
19
19
  export declare function readHeartbeatsFromIndexedDB(app: FirebaseApp): Promise<HeartbeatsInIndexedDB | undefined>;
20
20
  export declare function writeHeartbeatsToIndexedDB(app: FirebaseApp, heartbeatObject: HeartbeatsInIndexedDB): Promise<void>;
21
- export declare function deleteHeartbeatsFromIndexedDB(app: FirebaseApp): Promise<void>;
@@ -37,20 +37,19 @@ export interface HeartbeatService {
37
37
  getHeartbeatsHeader(): Promise<string>;
38
38
  }
39
39
  export interface HeartbeatsByUserAgent {
40
- userAgent: string;
40
+ agent: string;
41
41
  dates: string[];
42
42
  }
43
43
  export interface SingleDateHeartbeat {
44
- userAgent: string;
44
+ agent: string;
45
45
  date: string;
46
46
  }
47
47
  export interface HeartbeatStorage {
48
- overwrite(heartbeats: SingleDateHeartbeat[]): Promise<void>;
49
- add(heartbeats: SingleDateHeartbeat[]): Promise<void>;
50
- delete(heartbeats: SingleDateHeartbeat[]): Promise<void>;
51
- deleteAll(): Promise<void>;
52
- read(): Promise<SingleDateHeartbeat[]>;
48
+ overwrite(heartbeats: HeartbeatsInIndexedDB): Promise<void>;
49
+ add(heartbeats: HeartbeatsInIndexedDB): Promise<void>;
50
+ read(): Promise<HeartbeatsInIndexedDB>;
53
51
  }
54
52
  export interface HeartbeatsInIndexedDB {
53
+ lastSentHeartbeatDate?: string;
55
54
  heartbeats: SingleDateHeartbeat[];
56
55
  }
@@ -16,7 +16,7 @@
16
16
  */
17
17
  import { ComponentContainer } from '@firebase/component';
18
18
  import { FirebaseApp } from './public-types';
19
- import { HeartbeatsByUserAgent, HeartbeatService, HeartbeatStorage, SingleDateHeartbeat } from './types';
19
+ import { HeartbeatsByUserAgent, HeartbeatService, HeartbeatsInIndexedDB, HeartbeatStorage, SingleDateHeartbeat } from './types';
20
20
  export declare class HeartbeatServiceImpl implements HeartbeatService {
21
21
  private readonly container;
22
22
  /**
@@ -33,14 +33,14 @@ export declare class HeartbeatServiceImpl implements HeartbeatService {
33
33
  * be kept in sync with indexedDB.
34
34
  * Leave public for easier testing.
35
35
  */
36
- _heartbeatsCache: SingleDateHeartbeat[] | null;
36
+ _heartbeatsCache: HeartbeatsInIndexedDB | null;
37
37
  /**
38
38
  * the initialization promise for populating heartbeatCache.
39
39
  * If getHeartbeatsHeader() is called before the promise resolves
40
40
  * (hearbeatsCache == null), it should wait for this promise
41
41
  * Leave public for easier testing.
42
42
  */
43
- _heartbeatsCachePromise: Promise<SingleDateHeartbeat[]>;
43
+ _heartbeatsCachePromise: Promise<HeartbeatsInIndexedDB>;
44
44
  constructor(container: ComponentContainer);
45
45
  /**
46
46
  * Called to report a heartbeat. The function will generate
@@ -54,7 +54,8 @@ export declare class HeartbeatServiceImpl implements HeartbeatService {
54
54
  * Returns a base64 encoded string which can be attached to the heartbeat-specific header directly.
55
55
  * It also clears all heartbeats from memory as well as in IndexedDB.
56
56
  *
57
- * NOTE: It will read heartbeats from the heartbeatsCache, instead of from indexedDB to reduce latency
57
+ * NOTE: Consuming product SDKs should not send the header if this method
58
+ * returns an empty string.
58
59
  */
59
60
  getHeartbeatsHeader(): Promise<string>;
60
61
  }
@@ -70,11 +71,9 @@ export declare class HeartbeatStorageImpl implements HeartbeatStorage {
70
71
  /**
71
72
  * Read all heartbeats.
72
73
  */
73
- read(): Promise<SingleDateHeartbeat[]>;
74
- overwrite(heartbeats: SingleDateHeartbeat[]): Promise<void>;
75
- add(heartbeats: SingleDateHeartbeat[]): Promise<void>;
76
- delete(heartbeats: SingleDateHeartbeat[]): Promise<void>;
77
- deleteAll(): Promise<void>;
74
+ read(): Promise<HeartbeatsInIndexedDB>;
75
+ overwrite(heartbeatsObject: HeartbeatsInIndexedDB): Promise<void>;
76
+ add(heartbeatsObject: HeartbeatsInIndexedDB): Promise<void>;
78
77
  }
79
78
  /**
80
79
  * Calculate bytes of a HeartbeatsByUserAgent array after being wrapped
@@ -18,4 +18,3 @@ import { FirebaseApp } from './public-types';
18
18
  import { HeartbeatsInIndexedDB } from './types';
19
19
  export declare function readHeartbeatsFromIndexedDB(app: FirebaseApp): Promise<HeartbeatsInIndexedDB | undefined>;
20
20
  export declare function writeHeartbeatsToIndexedDB(app: FirebaseApp, heartbeatObject: HeartbeatsInIndexedDB): Promise<void>;
21
- export declare function deleteHeartbeatsFromIndexedDB(app: FirebaseApp): Promise<void>;
@@ -37,20 +37,19 @@ export interface HeartbeatService {
37
37
  getHeartbeatsHeader(): Promise<string>;
38
38
  }
39
39
  export interface HeartbeatsByUserAgent {
40
- userAgent: string;
40
+ agent: string;
41
41
  dates: string[];
42
42
  }
43
43
  export interface SingleDateHeartbeat {
44
- userAgent: string;
44
+ agent: string;
45
45
  date: string;
46
46
  }
47
47
  export interface HeartbeatStorage {
48
- overwrite(heartbeats: SingleDateHeartbeat[]): Promise<void>;
49
- add(heartbeats: SingleDateHeartbeat[]): Promise<void>;
50
- delete(heartbeats: SingleDateHeartbeat[]): Promise<void>;
51
- deleteAll(): Promise<void>;
52
- read(): Promise<SingleDateHeartbeat[]>;
48
+ overwrite(heartbeats: HeartbeatsInIndexedDB): Promise<void>;
49
+ add(heartbeats: HeartbeatsInIndexedDB): Promise<void>;
50
+ read(): Promise<HeartbeatsInIndexedDB>;
53
51
  }
54
52
  export interface HeartbeatsInIndexedDB {
53
+ lastSentHeartbeatDate?: string;
55
54
  heartbeats: SingleDateHeartbeat[];
56
55
  }
@@ -1,6 +1,6 @@
1
1
  import { Component, ComponentContainer } from '@firebase/component';
2
2
  import { Logger, setUserLogHandler, setLogLevel as setLogLevel$1 } from '@firebase/logger';
3
- import { ErrorFactory, deepEqual, base64Encode, isIndexedDBAvailable, validateIndexedDBOpenable } from '@firebase/util';
3
+ import { ErrorFactory, deepEqual, base64urlEncodeWithoutPadding, isIndexedDBAvailable, validateIndexedDBOpenable } from '@firebase/util';
4
4
  export { FirebaseError } from '@firebase/util';
5
5
  import { openDb } from 'idb';
6
6
 
@@ -58,7 +58,7 @@ function isVersionServiceProvider(provider) {
58
58
  }
59
59
 
60
60
  const name$o = "@firebase/app";
61
- const version$1 = "0.7.17-canary.2b98b28f2";
61
+ const version$1 = "0.7.17-canary.927c1afc1";
62
62
 
63
63
  /**
64
64
  * @license
@@ -125,7 +125,7 @@ const name$2 = "@firebase/firestore";
125
125
  const name$1 = "@firebase/firestore-compat";
126
126
 
127
127
  const name = "firebase";
128
- const version = "9.6.7-canary.2b98b28f2";
128
+ const version = "9.6.7-canary.927c1afc1";
129
129
 
130
130
  /**
131
131
  * @license
@@ -635,19 +635,6 @@ async function writeHeartbeatsToIndexedDB(app, heartbeatObject) {
635
635
  });
636
636
  }
637
637
  }
638
- async function deleteHeartbeatsFromIndexedDB(app) {
639
- try {
640
- const db = await getDbPromise();
641
- const tx = db.transaction(STORE_NAME, 'readwrite');
642
- await tx.objectStore(STORE_NAME).delete(computeKey(app));
643
- return tx.complete;
644
- }
645
- catch (e) {
646
- throw ERROR_FACTORY.create("storage-delete" /* STORAGE_DELETE */, {
647
- originalErrorMessage: e.message
648
- });
649
- }
650
- }
651
638
  function computeKey(app) {
652
639
  return `${app.name}!${app.options.appId}`;
653
640
  }
@@ -704,21 +691,23 @@ class HeartbeatServiceImpl {
704
691
  .getImmediate();
705
692
  // This is the "Firebase user agent" string from the platform logger
706
693
  // service, not the browser user agent.
707
- const userAgent = platformLogger.getPlatformInfoString();
694
+ const agent = platformLogger.getPlatformInfoString();
708
695
  const date = getUTCDateString();
709
696
  if (this._heartbeatsCache === null) {
710
697
  this._heartbeatsCache = await this._heartbeatsCachePromise;
711
698
  }
712
- if (this._heartbeatsCache.some(singleDateHeartbeat => singleDateHeartbeat.date === date)) {
713
- // Do not store a heartbeat if one is already stored for this day.
699
+ // Do not store a heartbeat if one is already stored for this day
700
+ // or if a header has already been sent today.
701
+ if (this._heartbeatsCache.lastSentHeartbeatDate === date ||
702
+ this._heartbeatsCache.heartbeats.some(singleDateHeartbeat => singleDateHeartbeat.date === date)) {
714
703
  return;
715
704
  }
716
705
  else {
717
706
  // There is no entry for this date. Create one.
718
- this._heartbeatsCache.push({ date, userAgent });
707
+ this._heartbeatsCache.heartbeats.push({ date, agent });
719
708
  }
720
709
  // Remove entries older than 30 days.
721
- this._heartbeatsCache = this._heartbeatsCache.filter(singleDateHeartbeat => {
710
+ this._heartbeatsCache.heartbeats = this._heartbeatsCache.heartbeats.filter(singleDateHeartbeat => {
722
711
  const hbTimestamp = new Date(singleDateHeartbeat.date).valueOf();
723
712
  const now = Date.now();
724
713
  return now - hbTimestamp <= STORED_HEARTBEAT_RETENTION_MAX_MILLIS;
@@ -729,31 +718,36 @@ class HeartbeatServiceImpl {
729
718
  * Returns a base64 encoded string which can be attached to the heartbeat-specific header directly.
730
719
  * It also clears all heartbeats from memory as well as in IndexedDB.
731
720
  *
732
- * NOTE: It will read heartbeats from the heartbeatsCache, instead of from indexedDB to reduce latency
721
+ * NOTE: Consuming product SDKs should not send the header if this method
722
+ * returns an empty string.
733
723
  */
734
724
  async getHeartbeatsHeader() {
735
725
  if (this._heartbeatsCache === null) {
736
726
  await this._heartbeatsCachePromise;
737
727
  }
738
- // If it's still null, it's been cleared and has not been repopulated.
739
- if (this._heartbeatsCache === null) {
728
+ // If it's still null or the array is empty, there is no data to send.
729
+ if (this._heartbeatsCache === null ||
730
+ this._heartbeatsCache.heartbeats.length === 0) {
740
731
  return '';
741
732
  }
733
+ const date = getUTCDateString();
742
734
  // Extract as many heartbeats from the cache as will fit under the size limit.
743
- const { heartbeatsToSend, unsentEntries } = extractHeartbeatsForHeader(this._heartbeatsCache);
744
- const headerString = base64Encode(JSON.stringify({ version: 2, heartbeats: heartbeatsToSend }));
735
+ const { heartbeatsToSend, unsentEntries } = extractHeartbeatsForHeader(this._heartbeatsCache.heartbeats);
736
+ const headerString = base64urlEncodeWithoutPadding(JSON.stringify({ version: 2, heartbeats: heartbeatsToSend }));
737
+ // Store last sent date to prevent another being logged/sent for the same day.
738
+ this._heartbeatsCache.lastSentHeartbeatDate = date;
745
739
  if (unsentEntries.length > 0) {
746
740
  // Store any unsent entries if they exist.
747
- this._heartbeatsCache = unsentEntries;
748
- // This seems more likely than deleteAll (below) to lead to some odd state
741
+ this._heartbeatsCache.heartbeats = unsentEntries;
742
+ // This seems more likely than emptying the array (below) to lead to some odd state
749
743
  // since the cache isn't empty and this will be called again on the next request,
750
744
  // and is probably safest if we await it.
751
745
  await this._storage.overwrite(this._heartbeatsCache);
752
746
  }
753
747
  else {
754
- this._heartbeatsCache = null;
748
+ this._heartbeatsCache.heartbeats = [];
755
749
  // Do not wait for this, to reduce latency.
756
- void this._storage.deleteAll();
750
+ void this._storage.overwrite(this._heartbeatsCache);
757
751
  }
758
752
  return headerString;
759
753
  }
@@ -771,11 +765,11 @@ function extractHeartbeatsForHeader(heartbeatsCache, maxSize = MAX_HEADER_BYTES)
771
765
  let unsentEntries = heartbeatsCache.slice();
772
766
  for (const singleDateHeartbeat of heartbeatsCache) {
773
767
  // Look for an existing entry with the same user agent.
774
- const heartbeatEntry = heartbeatsToSend.find(hb => hb.userAgent === singleDateHeartbeat.userAgent);
768
+ const heartbeatEntry = heartbeatsToSend.find(hb => hb.agent === singleDateHeartbeat.agent);
775
769
  if (!heartbeatEntry) {
776
770
  // If no entry for this user agent exists, create one.
777
771
  heartbeatsToSend.push({
778
- userAgent: singleDateHeartbeat.userAgent,
772
+ agent: singleDateHeartbeat.agent,
779
773
  dates: [singleDateHeartbeat.date]
780
774
  });
781
775
  if (countBytes(heartbeatsToSend) > maxSize) {
@@ -824,59 +818,46 @@ class HeartbeatStorageImpl {
824
818
  async read() {
825
819
  const canUseIndexedDB = await this._canUseIndexedDBPromise;
826
820
  if (!canUseIndexedDB) {
827
- return [];
821
+ return { heartbeats: [] };
828
822
  }
829
823
  else {
830
824
  const idbHeartbeatObject = await readHeartbeatsFromIndexedDB(this.app);
831
- return (idbHeartbeatObject === null || idbHeartbeatObject === void 0 ? void 0 : idbHeartbeatObject.heartbeats) || [];
825
+ return idbHeartbeatObject || { heartbeats: [] };
832
826
  }
833
827
  }
834
828
  // overwrite the storage with the provided heartbeats
835
- async overwrite(heartbeats) {
836
- const canUseIndexedDB = await this._canUseIndexedDBPromise;
837
- if (!canUseIndexedDB) {
838
- return;
839
- }
840
- else {
841
- return writeHeartbeatsToIndexedDB(this.app, { heartbeats });
842
- }
843
- }
844
- // add heartbeats
845
- async add(heartbeats) {
829
+ async overwrite(heartbeatsObject) {
830
+ var _a;
846
831
  const canUseIndexedDB = await this._canUseIndexedDBPromise;
847
832
  if (!canUseIndexedDB) {
848
833
  return;
849
834
  }
850
835
  else {
851
- const existingHeartbeats = await this.read();
836
+ const existingHeartbeatsObject = await this.read();
852
837
  return writeHeartbeatsToIndexedDB(this.app, {
853
- heartbeats: [...existingHeartbeats, ...heartbeats]
838
+ lastSentHeartbeatDate: (_a = heartbeatsObject.lastSentHeartbeatDate) !== null && _a !== void 0 ? _a : existingHeartbeatsObject.lastSentHeartbeatDate,
839
+ heartbeats: heartbeatsObject.heartbeats
854
840
  });
855
841
  }
856
842
  }
857
- // delete heartbeats
858
- async delete(heartbeats) {
843
+ // add heartbeats
844
+ async add(heartbeatsObject) {
845
+ var _a;
859
846
  const canUseIndexedDB = await this._canUseIndexedDBPromise;
860
847
  if (!canUseIndexedDB) {
861
848
  return;
862
849
  }
863
850
  else {
864
- const existingHeartbeats = await this.read();
851
+ const existingHeartbeatsObject = await this.read();
865
852
  return writeHeartbeatsToIndexedDB(this.app, {
866
- heartbeats: existingHeartbeats.filter(existingHeartbeat => !heartbeats.includes(existingHeartbeat))
853
+ lastSentHeartbeatDate: (_a = heartbeatsObject.lastSentHeartbeatDate) !== null && _a !== void 0 ? _a : existingHeartbeatsObject.lastSentHeartbeatDate,
854
+ heartbeats: [
855
+ ...existingHeartbeatsObject.heartbeats,
856
+ ...heartbeatsObject.heartbeats
857
+ ]
867
858
  });
868
859
  }
869
860
  }
870
- // delete all heartbeats
871
- async deleteAll() {
872
- const canUseIndexedDB = await this._canUseIndexedDBPromise;
873
- if (!canUseIndexedDB) {
874
- return;
875
- }
876
- else {
877
- return deleteHeartbeatsFromIndexedDB(this.app);
878
- }
879
- }
880
861
  }
881
862
  /**
882
863
  * Calculate bytes of a HeartbeatsByUserAgent array after being wrapped
@@ -885,7 +866,7 @@ class HeartbeatStorageImpl {
885
866
  */
886
867
  function countBytes(heartbeatsCache) {
887
868
  // base64 has a restricted set of characters, all of which should be 1 byte.
888
- return base64Encode(
869
+ return base64urlEncodeWithoutPadding(
889
870
  // heartbeatsCache wrapper properties
890
871
  JSON.stringify({ version: 2, heartbeats: heartbeatsCache })).length;
891
872
  }