Skip to content

Commit 0fbf345

Browse files
committed
Add integration for offline support
Offline events are cached in the browser with localforage.
1 parent a19e33e commit 0fbf345

File tree

4 files changed

+156
-1
lines changed

4 files changed

+156
-1
lines changed

packages/browser/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
},
1818
"dependencies": {
1919
"@sentry/core": "5.20.1",
20+
"@sentry/minimal": "5.20.1",
2021
"@sentry/types": "5.20.1",
2122
"@sentry/utils": "5.20.1",
23+
"localforage": "^1.8.1",
2224
"tslib": "^1.9.3"
2325
},
2426
"devDependencies": {

packages/browser/src/integrations/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export { TryCatch } from './trycatch';
33
export { Breadcrumbs } from './breadcrumbs';
44
export { LinkedErrors } from './linkederrors';
55
export { UserAgent } from './useragent';
6+
export { Offline } from './offline';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import { addGlobalEventProcessor, getCurrentHub } from '@sentry/core';
2+
import { captureEvent } from '@sentry/minimal';
3+
import { Event, Integration } from '@sentry/types';
4+
import { getGlobalObject, uuid4 } from '@sentry/utils';
5+
// @ts-ignore
6+
import localforage = require('localforage');
7+
8+
/**
9+
* cache offline errors and send when connected
10+
*/
11+
export class Offline implements Integration {
12+
/**
13+
* @inheritDoc
14+
*/
15+
public static id: string = 'Offline';
16+
17+
/**
18+
* @inheritDoc
19+
*/
20+
public readonly name: string = Offline.id;
21+
22+
/**
23+
* event cache
24+
*/
25+
public offlineEventStore: LocalForage;
26+
27+
/**
28+
* @inheritDoc
29+
*/
30+
public constructor() {
31+
this.offlineEventStore = localforage.createInstance({
32+
name: 'sentry/offlineEventStore',
33+
});
34+
35+
getGlobalObject<Window>().addEventListener('online', () => {
36+
this._sendEvents().catch(() => {
37+
// todo: handle localforage error
38+
});
39+
});
40+
}
41+
42+
/**
43+
* @inheritDoc
44+
*/
45+
public setupOnce(): void {
46+
addGlobalEventProcessor(async (event: Event) => {
47+
if (getCurrentHub().getIntegration(Offline)) {
48+
const global = getGlobalObject<Window>();
49+
50+
// cache if we are positively offline
51+
if ('navigator' in global && 'onLine' in global.navigator && !global.navigator.onLine) {
52+
try {
53+
await this._cacheEvent(event);
54+
} catch (error) {
55+
// todo: handle localforage error
56+
}
57+
58+
// return null on success or failure, because being offline will still result in an error
59+
return null;
60+
}
61+
}
62+
63+
return event;
64+
});
65+
}
66+
67+
/**
68+
* cache an event to send later
69+
* @param event an event
70+
*/
71+
private async _cacheEvent(event: Event): Promise<Event> {
72+
return this.offlineEventStore.setItem(uuid4(), event);
73+
}
74+
75+
/**
76+
* purge event from cache
77+
*/
78+
private async _purgeEvent(cacheKey: string): Promise<void> {
79+
return this.offlineEventStore.removeItem(cacheKey);
80+
}
81+
82+
/**
83+
* send all events
84+
*/
85+
private async _sendEvents(): Promise<void> {
86+
return this.offlineEventStore.iterate((event: any, cacheKey: string, _index: number) => {
87+
try {
88+
const newEventId = captureEvent(event);
89+
90+
if (newEventId) {
91+
this._purgeEvent(cacheKey)
92+
.then(_ => _)
93+
.catch(_ => _);
94+
}
95+
} catch (error) {
96+
// handle JSON.parse error
97+
}
98+
});
99+
}
100+
}

yarn.lock

+53-1
Original file line numberDiff line numberDiff line change
@@ -1939,11 +1939,32 @@ [email protected]:
19391939
resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f"
19401940
integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=
19411941

1942-
agent-base@4, agent-base@5, agent-base@6, agent-base@^4.3.0, agent-base@~4.2.1:
1942+
agent-base@4, agent-base@^4.3.0:
1943+
version "4.3.0"
1944+
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee"
1945+
integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==
1946+
dependencies:
1947+
es6-promisify "^5.0.0"
1948+
1949+
agent-base@5:
19431950
version "5.1.1"
19441951
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c"
19451952
integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==
19461953

1954+
agent-base@6:
1955+
version "6.0.1"
1956+
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.1.tgz#808007e4e5867decb0ab6ab2f928fbdb5a596db4"
1957+
integrity sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg==
1958+
dependencies:
1959+
debug "4"
1960+
1961+
agent-base@~4.2.1:
1962+
version "4.2.1"
1963+
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
1964+
integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==
1965+
dependencies:
1966+
es6-promisify "^5.0.0"
1967+
19471968
agentkeepalive@^3.4.1:
19481969
version "3.5.2"
19491970
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67"
@@ -5114,6 +5135,18 @@ es6-object-assign@^1.1.0:
51145135
resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
51155136
integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
51165137

5138+
es6-promise@^4.0.3:
5139+
version "4.2.8"
5140+
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
5141+
integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==
5142+
5143+
es6-promisify@^5.0.0:
5144+
version "5.0.0"
5145+
resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203"
5146+
integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=
5147+
dependencies:
5148+
es6-promise "^4.0.3"
5149+
51175150
escalade@^3.0.1:
51185151
version "3.0.2"
51195152
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4"
@@ -6528,6 +6561,11 @@ ignore@^5.1.4:
65286561
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
65296562
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
65306563

6564+
immediate@~3.0.5:
6565+
version "3.0.6"
6566+
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
6567+
integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
6568+
65316569
import-fresh@^2.0.0:
65326570
version "2.0.0"
65336571
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
@@ -8106,6 +8144,13 @@ libnpmpublish@^1.1.1:
81068144
semver "^5.5.1"
81078145
ssri "^6.0.1"
81088146

8147+
8148+
version "3.1.1"
8149+
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
8150+
integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
8151+
dependencies:
8152+
immediate "~3.0.5"
8153+
81098154
lines-and-columns@^1.1.6:
81108155
version "1.1.6"
81118156
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
@@ -8155,6 +8200,13 @@ loader-utils@^2.0.0:
81558200
emojis-list "^3.0.0"
81568201
json5 "^2.1.2"
81578202

8203+
localforage@^1.8.1:
8204+
version "1.8.1"
8205+
resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.8.1.tgz#f6c0a24b41ab33b10e4dc84342dd696f6f3e3433"
8206+
integrity sha512-azSSJJfc7h4bVpi0PGi+SmLQKJl2/8NErI+LhJsrORNikMZnhaQ7rv9fHj+ofwgSHrKRlsDCL/639a6nECIKuQ==
8207+
dependencies:
8208+
lie "3.1.1"
8209+
81588210
locate-path@^2.0.0:
81598211
version "2.0.0"
81608212
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"

0 commit comments

Comments
 (0)