@thisbefine/analytics 0.2.0 → 0.3.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/dist/core/analytics.d.ts +44 -2
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/core/analytics.js +154 -20
- package/dist/core/analytics.js.map +1 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +3 -2
- package/dist/core/errors.js.map +1 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +0 -6
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/logger.d.ts +2 -1
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +53 -1
- package/dist/core/logger.js.map +1 -1
- package/dist/core/privacy.d.ts +50 -2
- package/dist/core/privacy.d.ts.map +1 -1
- package/dist/core/privacy.js +118 -1
- package/dist/core/privacy.js.map +1 -1
- package/dist/core/queue.d.ts +76 -3
- package/dist/core/queue.d.ts.map +1 -1
- package/dist/core/queue.js +353 -40
- package/dist/core/queue.js.map +1 -1
- package/dist/core/session.d.ts +5 -3
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +20 -5
- package/dist/core/session.js.map +1 -1
- package/dist/core/types.d.ts +150 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +10 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/next.d.ts +4 -1
- package/dist/next.d.ts.map +1 -1
- package/dist/next.js +2 -1
- package/dist/next.js.map +1 -1
- package/dist/react/context.d.ts +95 -0
- package/dist/react/context.d.ts.map +1 -0
- package/dist/react/context.js +120 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/hooks.d.ts.map +1 -1
- package/dist/react/hooks.js +38 -37
- package/dist/react/hooks.js.map +1 -1
- package/dist/react.d.ts +2 -0
- package/dist/react.d.ts.map +1 -1
- package/dist/react.js +2 -3
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACtB,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAClC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACnC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACpC;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACtB,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IAClC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;IACnC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACpC;AAyCD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,YAAY,GACxB,QAAQ,MAAM,EACd,OAAO,OAAO,EACd,oBAAkB,KAChB,MAuDF,CAAC"}
|
package/dist/core/logger.js
CHANGED
|
@@ -1,8 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format arguments into a message string and optional data
|
|
3
|
+
*/
|
|
4
|
+
const formatArgs = (args) => {
|
|
5
|
+
if (args.length === 0) {
|
|
6
|
+
return { message: "" };
|
|
7
|
+
}
|
|
8
|
+
const message = String(args[0]);
|
|
9
|
+
if (args.length === 1) {
|
|
10
|
+
return { message };
|
|
11
|
+
}
|
|
12
|
+
if (args.length === 2 && typeof args[1] === "object" && args[1] !== null) {
|
|
13
|
+
return { message, data: args[1] };
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
message: args
|
|
17
|
+
.map((arg) => typeof arg === "object" ? JSON.stringify(arg) : String(arg))
|
|
18
|
+
.join(" "),
|
|
19
|
+
data: args.slice(1),
|
|
20
|
+
};
|
|
21
|
+
};
|
|
1
22
|
/**
|
|
2
23
|
* Create a namespaced debug logger
|
|
3
24
|
*
|
|
4
25
|
* @param prefix - Module name (e.g., "Queue", "Session", "Errors")
|
|
5
26
|
* @param debug - Whether debug mode is enabled
|
|
27
|
+
* @param structured - Whether to output structured JSON logs
|
|
6
28
|
* @returns Logger interface with log, warn, and error methods
|
|
7
29
|
*
|
|
8
30
|
* @example
|
|
@@ -13,8 +35,38 @@
|
|
|
13
35
|
* logger.error("Failed to send batch:", error);
|
|
14
36
|
* ```
|
|
15
37
|
*/
|
|
16
|
-
export const createLogger = (prefix, debug) => {
|
|
38
|
+
export const createLogger = (prefix, debug, structured = false) => {
|
|
17
39
|
const shouldLog = debug && typeof console !== "undefined";
|
|
40
|
+
const logStructured = (level, args) => {
|
|
41
|
+
const { message, data } = formatArgs(args);
|
|
42
|
+
const entry = {
|
|
43
|
+
timestamp: new Date().toISOString(),
|
|
44
|
+
level,
|
|
45
|
+
module: prefix,
|
|
46
|
+
message,
|
|
47
|
+
...(data !== undefined && { data }),
|
|
48
|
+
};
|
|
49
|
+
console.log(JSON.stringify(entry));
|
|
50
|
+
};
|
|
51
|
+
if (structured) {
|
|
52
|
+
return {
|
|
53
|
+
log: (...args) => {
|
|
54
|
+
if (shouldLog) {
|
|
55
|
+
logStructured("debug", args);
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
warn: (...args) => {
|
|
59
|
+
if (shouldLog) {
|
|
60
|
+
logStructured("warn", args);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
error: (...args) => {
|
|
64
|
+
if (typeof console !== "undefined") {
|
|
65
|
+
logStructured("error", args);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
18
70
|
return {
|
|
19
71
|
log: (...args) => {
|
|
20
72
|
if (shouldLog) {
|
package/dist/core/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"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"}
|
package/dist/core/privacy.d.ts
CHANGED
|
@@ -1,15 +1,30 @@
|
|
|
1
|
-
import type { StorageInterface } from "./types";
|
|
1
|
+
import type { ConsentCategory, ConsentConfig, StorageInterface } from "./types";
|
|
2
2
|
/**
|
|
3
3
|
* Privacy manager handles:
|
|
4
4
|
* - Do Not Track (DNT) detection
|
|
5
5
|
* - Global Privacy Control (GPC) detection
|
|
6
6
|
* - User opt-out/opt-in state
|
|
7
|
+
* - Granular consent categories (analytics, marketing, functional)
|
|
7
8
|
*/
|
|
8
9
|
export declare class Privacy {
|
|
9
10
|
private storage;
|
|
10
11
|
private respectDNT;
|
|
11
12
|
private debug;
|
|
12
|
-
|
|
13
|
+
private consentConfig;
|
|
14
|
+
private consentCategories;
|
|
15
|
+
constructor(storage: StorageInterface, respectDNT: boolean, debug?: boolean, consentConfig?: ConsentConfig);
|
|
16
|
+
/**
|
|
17
|
+
* Load consent categories from storage.
|
|
18
|
+
* Fails safe: if stored consent is corrupted, returns empty set (no consent)
|
|
19
|
+
* rather than defaulting to consent, to protect user privacy.
|
|
20
|
+
*/
|
|
21
|
+
private loadConsentFromStorage;
|
|
22
|
+
/**
|
|
23
|
+
* Save consent categories to storage.
|
|
24
|
+
* Returns true if saved successfully, false otherwise.
|
|
25
|
+
* Warns on failure since consent preferences may not persist across sessions.
|
|
26
|
+
*/
|
|
27
|
+
private saveConsentToStorage;
|
|
13
28
|
/**
|
|
14
29
|
* Check if Do Not Track is enabled in the browser
|
|
15
30
|
*/
|
|
@@ -35,6 +50,38 @@ export declare class Privacy {
|
|
|
35
50
|
* Check if tracking should be allowed based on all privacy signals
|
|
36
51
|
*/
|
|
37
52
|
shouldTrack(): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Check if tracking is allowed for a specific consent category
|
|
55
|
+
*/
|
|
56
|
+
shouldTrackForCategory(category: ConsentCategory): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Check if a specific consent category is enabled
|
|
59
|
+
*/
|
|
60
|
+
hasConsent(category: ConsentCategory): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Get all currently consented categories
|
|
63
|
+
*/
|
|
64
|
+
getConsentedCategories(): ConsentCategory[];
|
|
65
|
+
/**
|
|
66
|
+
* Set consent for specific categories (replaces current consent)
|
|
67
|
+
*/
|
|
68
|
+
setConsent(categories: ConsentCategory[]): void;
|
|
69
|
+
/**
|
|
70
|
+
* Grant consent for a specific category
|
|
71
|
+
*/
|
|
72
|
+
grantConsent(category: ConsentCategory): void;
|
|
73
|
+
/**
|
|
74
|
+
* Revoke consent for a specific category
|
|
75
|
+
*/
|
|
76
|
+
revokeConsent(category: ConsentCategory): void;
|
|
77
|
+
/**
|
|
78
|
+
* Grant consent for all categories
|
|
79
|
+
*/
|
|
80
|
+
grantAllConsent(): void;
|
|
81
|
+
/**
|
|
82
|
+
* Revoke consent for all categories
|
|
83
|
+
*/
|
|
84
|
+
revokeAllConsent(): void;
|
|
38
85
|
/**
|
|
39
86
|
* Get a summary of privacy signals for debugging
|
|
40
87
|
*/
|
|
@@ -44,6 +91,7 @@ export declare class Privacy {
|
|
|
44
91
|
optedOut: boolean;
|
|
45
92
|
respectDNT: boolean;
|
|
46
93
|
trackingAllowed: boolean;
|
|
94
|
+
consentCategories: ConsentCategory[];
|
|
47
95
|
};
|
|
48
96
|
/**
|
|
49
97
|
* Debug logger
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../src/core/privacy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"privacy.d.ts","sourceRoot":"","sources":["../../src/core/privacy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAahF;;;;;;GAMG;AACH,qBAAa,OAAO;IACnB,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,UAAU,CAAU;IAC5B,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,iBAAiB,CAAuB;gBAG/C,OAAO,EAAE,gBAAgB,EACzB,UAAU,EAAE,OAAO,EACnB,KAAK,UAAQ,EACb,aAAa,CAAC,EAAE,aAAa;IAU9B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IA4B9B;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAgB5B;;OAEG;IACH,YAAY,IAAI,OAAO;IAWvB;;;OAGG;IACH,YAAY,IAAI,OAAO;IASvB;;OAEG;IACH,UAAU,IAAI,OAAO;IAIrB;;OAEG;IACH,MAAM,IAAI,IAAI;IAKd;;OAEG;IACH,KAAK,IAAI,IAAI;IAKb;;OAEG;IACH,WAAW,IAAI,OAAO;IAmBtB;;OAEG;IACH,sBAAsB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO;IAO1D;;OAEG;IACH,UAAU,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO;IAI9C;;OAEG;IACH,sBAAsB,IAAI,eAAe,EAAE;IAI3C;;OAEG;IACH,UAAU,CAAC,UAAU,EAAE,eAAe,EAAE,GAAG,IAAI;IAQ/C;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAQ7C;;OAEG;IACH,aAAa,CAAC,QAAQ,EAAE,eAAe,GAAG,IAAI;IAM9C;;OAEG;IACH,eAAe,IAAI,IAAI;IAMvB;;OAEG;IACH,gBAAgB,IAAI,IAAI;IAMxB;;OAEG;IACH,gBAAgB,IAAI;QACnB,UAAU,EAAE,OAAO,CAAC;QACpB,UAAU,EAAE,OAAO,CAAC;QACpB,QAAQ,EAAE,OAAO,CAAC;QAClB,UAAU,EAAE,OAAO,CAAC;QACpB,eAAe,EAAE,OAAO,CAAC;QACzB,iBAAiB,EAAE,eAAe,EAAE,CAAC;KACrC;IAWD;;OAEG;IACH,OAAO,CAAC,GAAG;CAKX"}
|
package/dist/core/privacy.js
CHANGED
|
@@ -1,15 +1,68 @@
|
|
|
1
1
|
import { STORAGE_KEYS } from "./types";
|
|
2
|
+
/** Storage key for consent categories */
|
|
3
|
+
const CONSENT_KEY = "tif_consent";
|
|
4
|
+
/** All available consent categories */
|
|
5
|
+
const ALL_CATEGORIES = [
|
|
6
|
+
"analytics",
|
|
7
|
+
"marketing",
|
|
8
|
+
"functional",
|
|
9
|
+
];
|
|
2
10
|
/**
|
|
3
11
|
* Privacy manager handles:
|
|
4
12
|
* - Do Not Track (DNT) detection
|
|
5
13
|
* - Global Privacy Control (GPC) detection
|
|
6
14
|
* - User opt-out/opt-in state
|
|
15
|
+
* - Granular consent categories (analytics, marketing, functional)
|
|
7
16
|
*/
|
|
8
17
|
export class Privacy {
|
|
9
|
-
constructor(storage, respectDNT, debug = false) {
|
|
18
|
+
constructor(storage, respectDNT, debug = false, consentConfig) {
|
|
10
19
|
this.storage = storage;
|
|
11
20
|
this.respectDNT = respectDNT;
|
|
12
21
|
this.debug = debug;
|
|
22
|
+
this.consentConfig = consentConfig ?? {};
|
|
23
|
+
this.consentCategories = this.loadConsentFromStorage();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Load consent categories from storage.
|
|
27
|
+
* Fails safe: if stored consent is corrupted, returns empty set (no consent)
|
|
28
|
+
* rather than defaulting to consent, to protect user privacy.
|
|
29
|
+
*/
|
|
30
|
+
loadConsentFromStorage() {
|
|
31
|
+
try {
|
|
32
|
+
const stored = this.storage.get(CONSENT_KEY);
|
|
33
|
+
if (stored) {
|
|
34
|
+
const parsed = JSON.parse(stored);
|
|
35
|
+
if (Array.isArray(parsed)) {
|
|
36
|
+
return new Set(parsed.filter((c) => ALL_CATEGORIES.includes(c)));
|
|
37
|
+
}
|
|
38
|
+
console.warn("[Thisbefine Privacy] Stored consent data is invalid, failing safe with no consent");
|
|
39
|
+
return new Set();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.warn("[Thisbefine Privacy] Failed to load consent from storage, failing safe with no consent:", error);
|
|
44
|
+
return new Set();
|
|
45
|
+
}
|
|
46
|
+
const defaultConsent = this.consentConfig.defaultConsent ?? true;
|
|
47
|
+
if (defaultConsent) {
|
|
48
|
+
return new Set(this.consentConfig.categories ?? ALL_CATEGORIES);
|
|
49
|
+
}
|
|
50
|
+
return new Set();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Save consent categories to storage.
|
|
54
|
+
* Returns true if saved successfully, false otherwise.
|
|
55
|
+
* Warns on failure since consent preferences may not persist across sessions.
|
|
56
|
+
*/
|
|
57
|
+
saveConsentToStorage() {
|
|
58
|
+
try {
|
|
59
|
+
this.storage.set(CONSENT_KEY, JSON.stringify([...this.consentCategories]));
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
console.warn("[Thisbefine Privacy] Failed to persist consent to storage. User consent preference may not persist across sessions:", error);
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
13
66
|
}
|
|
14
67
|
/**
|
|
15
68
|
* Check if Do Not Track is enabled in the browser
|
|
@@ -70,6 +123,69 @@ export class Privacy {
|
|
|
70
123
|
}
|
|
71
124
|
return true;
|
|
72
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if tracking is allowed for a specific consent category
|
|
128
|
+
*/
|
|
129
|
+
shouldTrackForCategory(category) {
|
|
130
|
+
if (!this.shouldTrack()) {
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
return this.consentCategories.has(category);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if a specific consent category is enabled
|
|
137
|
+
*/
|
|
138
|
+
hasConsent(category) {
|
|
139
|
+
return this.consentCategories.has(category);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Get all currently consented categories
|
|
143
|
+
*/
|
|
144
|
+
getConsentedCategories() {
|
|
145
|
+
return [...this.consentCategories];
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Set consent for specific categories (replaces current consent)
|
|
149
|
+
*/
|
|
150
|
+
setConsent(categories) {
|
|
151
|
+
this.consentCategories = new Set(categories.filter((c) => ALL_CATEGORIES.includes(c)));
|
|
152
|
+
this.saveConsentToStorage();
|
|
153
|
+
this.log("Consent updated:", [...this.consentCategories]);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Grant consent for a specific category
|
|
157
|
+
*/
|
|
158
|
+
grantConsent(category) {
|
|
159
|
+
if (ALL_CATEGORIES.includes(category)) {
|
|
160
|
+
this.consentCategories.add(category);
|
|
161
|
+
this.saveConsentToStorage();
|
|
162
|
+
this.log("Consent granted:", category);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Revoke consent for a specific category
|
|
167
|
+
*/
|
|
168
|
+
revokeConsent(category) {
|
|
169
|
+
this.consentCategories.delete(category);
|
|
170
|
+
this.saveConsentToStorage();
|
|
171
|
+
this.log("Consent revoked:", category);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Grant consent for all categories
|
|
175
|
+
*/
|
|
176
|
+
grantAllConsent() {
|
|
177
|
+
this.consentCategories = new Set(ALL_CATEGORIES);
|
|
178
|
+
this.saveConsentToStorage();
|
|
179
|
+
this.log("All consent granted");
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Revoke consent for all categories
|
|
183
|
+
*/
|
|
184
|
+
revokeAllConsent() {
|
|
185
|
+
this.consentCategories.clear();
|
|
186
|
+
this.saveConsentToStorage();
|
|
187
|
+
this.log("All consent revoked");
|
|
188
|
+
}
|
|
73
189
|
/**
|
|
74
190
|
* Get a summary of privacy signals for debugging
|
|
75
191
|
*/
|
|
@@ -80,6 +196,7 @@ export class Privacy {
|
|
|
80
196
|
optedOut: this.isOptedOut(),
|
|
81
197
|
respectDNT: this.respectDNT,
|
|
82
198
|
trackingAllowed: this.shouldTrack(),
|
|
199
|
+
consentCategories: this.getConsentedCategories(),
|
|
83
200
|
};
|
|
84
201
|
}
|
|
85
202
|
/**
|
package/dist/core/privacy.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"privacy.js","sourceRoot":"","sources":["../../src/core/privacy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC
|
|
1
|
+
{"version":3,"file":"privacy.js","sourceRoot":"","sources":["../../src/core/privacy.ts"],"names":[],"mappings":"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"}
|
package/dist/core/queue.d.ts
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
|
-
import type { AnalyticsEvent, ResolvedConfig } from "./types";
|
|
1
|
+
import type { AnalyticsEvent, FlushResult, ResolvedConfig } from "./types";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Circuit breaker state
|
|
4
|
+
*/
|
|
5
|
+
type CircuitState = "closed" | "open" | "half-open";
|
|
6
|
+
/**
|
|
7
|
+
* Event queue with batching, automatic flushing, retry logic, offline detection,
|
|
8
|
+
* persistent storage for crash recovery, and circuit breaker pattern.
|
|
4
9
|
*
|
|
5
10
|
* Features:
|
|
6
11
|
* - Batches events up to flushAt threshold
|
|
7
12
|
* - Flushes automatically after flushInterval
|
|
8
13
|
* - Uses sendBeacon on page unload for reliability
|
|
9
14
|
* - Exponential backoff retry on failure
|
|
15
|
+
* - Pauses when offline, resumes when online
|
|
16
|
+
* - Persists queue to localStorage for crash recovery
|
|
17
|
+
* - Calls onFlushError callback on failures
|
|
18
|
+
* - Circuit breaker to stop hammering failed servers
|
|
10
19
|
*/
|
|
11
20
|
export declare class Queue {
|
|
12
21
|
private items;
|
|
@@ -15,11 +24,34 @@ export declare class Queue {
|
|
|
15
24
|
private visibilityFlushPending;
|
|
16
25
|
private config;
|
|
17
26
|
private logger;
|
|
27
|
+
private destroyed;
|
|
28
|
+
private isOnline;
|
|
29
|
+
private circuitState;
|
|
30
|
+
private consecutiveFailures;
|
|
31
|
+
private circuitOpenedAt;
|
|
32
|
+
private clockOffset;
|
|
33
|
+
private boundVisibilityHandler;
|
|
34
|
+
private boundBeforeUnloadHandler;
|
|
35
|
+
private boundPageHideHandler;
|
|
36
|
+
private boundOnlineHandler;
|
|
37
|
+
private boundOfflineHandler;
|
|
18
38
|
constructor(config: ResolvedConfig);
|
|
19
39
|
/**
|
|
20
40
|
* Set up handlers for page unload to ensure events are sent
|
|
21
41
|
*/
|
|
22
42
|
private setupUnloadHandlers;
|
|
43
|
+
/**
|
|
44
|
+
* Set up handlers for online/offline detection
|
|
45
|
+
*/
|
|
46
|
+
private setupOnlineHandlers;
|
|
47
|
+
/**
|
|
48
|
+
* Restore queue from persistent storage (crash recovery)
|
|
49
|
+
*/
|
|
50
|
+
private restoreFromStorage;
|
|
51
|
+
/**
|
|
52
|
+
* Persist queue to storage for crash recovery
|
|
53
|
+
*/
|
|
54
|
+
private persistToStorage;
|
|
23
55
|
/**
|
|
24
56
|
* Add an event to the queue
|
|
25
57
|
*/
|
|
@@ -29,10 +61,41 @@ export declare class Queue {
|
|
|
29
61
|
*
|
|
30
62
|
* @param useBeacon - Use sendBeacon for page unload scenarios
|
|
31
63
|
* @param depth - Internal recursion depth counter (prevents stack overflow)
|
|
64
|
+
* @returns FlushResult with success status and any errors
|
|
65
|
+
*/
|
|
66
|
+
flush(useBeacon?: boolean, depth?: number): Promise<FlushResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Notify error callback of flush failure
|
|
69
|
+
*/
|
|
70
|
+
private notifyError;
|
|
71
|
+
/**
|
|
72
|
+
* Check if we can attempt a request based on circuit breaker state
|
|
73
|
+
*/
|
|
74
|
+
private canAttemptRequest;
|
|
75
|
+
/**
|
|
76
|
+
* Record a successful request, closing the circuit
|
|
77
|
+
*/
|
|
78
|
+
private recordSuccess;
|
|
79
|
+
/**
|
|
80
|
+
* Record a failed request, potentially opening the circuit
|
|
81
|
+
*/
|
|
82
|
+
private recordFailure;
|
|
83
|
+
/**
|
|
84
|
+
* Get the current circuit breaker state (for testing/debugging)
|
|
85
|
+
*/
|
|
86
|
+
get circuitBreakerState(): CircuitState;
|
|
87
|
+
/**
|
|
88
|
+
* Update clock offset from server response
|
|
89
|
+
* Uses the Date header to calculate the difference between client and server time
|
|
32
90
|
*/
|
|
33
|
-
|
|
91
|
+
private updateClockOffset;
|
|
92
|
+
/**
|
|
93
|
+
* Get the current clock offset (for testing/debugging)
|
|
94
|
+
*/
|
|
95
|
+
getClockOffset(): number;
|
|
34
96
|
/**
|
|
35
97
|
* Send events using sendBeacon (for page unload)
|
|
98
|
+
* @returns true if sendBeacon succeeded, false otherwise
|
|
36
99
|
*/
|
|
37
100
|
private sendWithBeacon;
|
|
38
101
|
/**
|
|
@@ -55,5 +118,15 @@ export declare class Queue {
|
|
|
55
118
|
* Get the number of events in the queue
|
|
56
119
|
*/
|
|
57
120
|
get length(): number;
|
|
121
|
+
/**
|
|
122
|
+
* Check if the queue is currently online
|
|
123
|
+
*/
|
|
124
|
+
get online(): boolean;
|
|
125
|
+
/**
|
|
126
|
+
* Destroy the queue, removing all event listeners and clearing timers.
|
|
127
|
+
* Attempts to flush remaining events before cleanup.
|
|
128
|
+
*/
|
|
129
|
+
destroy(): Promise<void>;
|
|
58
130
|
}
|
|
131
|
+
export {};
|
|
59
132
|
//# sourceMappingURL=queue.d.ts.map
|
package/dist/core/queue.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/core/queue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"queue.d.ts","sourceRoot":"","sources":["../../src/core/queue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG3E;;GAEG;AACH,KAAK,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAEpD;;;;;;;;;;;;;GAaG;AACH,qBAAa,KAAK;IACjB,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,KAAK,CAA8C;IAC3D,OAAO,CAAC,YAAY,CAAqC;IACzD,OAAO,CAAC,sBAAsB,CAAS;IACvC,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAQ;IAExB,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,eAAe,CAAuB;IAE9C,OAAO,CAAC,WAAW,CAAK;IAExB,OAAO,CAAC,sBAAsB,CAA6B;IAC3D,OAAO,CAAC,wBAAwB,CAA6B;IAC7D,OAAO,CAAC,oBAAoB,CAA6B;IACzD,OAAO,CAAC,kBAAkB,CAA6B;IACvD,OAAO,CAAC,mBAAmB,CAA6B;gBAE5C,MAAM,EAAE,cAAc;IAclC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA2B3B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAqB3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAoB1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAgBxB;;OAEG;IACH,IAAI,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IA0BjC;;;;;;OAMG;IACG,KAAK,CAAC,SAAS,UAAQ,EAAE,KAAK,SAAI,GAAG,OAAO,CAAC,WAAW,CAAC;IAwH/D;;OAEG;IACH,OAAO,CAAC,WAAW;IAUnB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkBzB;;OAEG;IACH,OAAO,CAAC,aAAa;IASrB;;OAEG;IACH,OAAO,CAAC,aAAa;IAarB;;OAEG;IACH,IAAI,mBAAmB,IAAI,YAAY,CAEtC;IAED;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAwBzB;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;;OAGG;IACH,OAAO,CAAC,cAAc;IAmBtB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAuBzB;;OAEG;YACW,aAAa;IAsE3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAwE1B;;OAEG;IACH,OAAO,CAAC,KAAK;IAIb;;OAEG;IACH,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAqD9B"}
|