@grainql/analytics-web 1.0.1 → 1.1.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/cjs/index.d.ts +6 -5
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.d.ts +6 -5
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.global.dev.js +47 -20
- package/dist/index.global.dev.js.map +2 -2
- package/dist/index.global.js +2 -2
- package/dist/index.global.js.map +3 -3
- package/dist/index.js +56 -23
- package/dist/index.mjs +56 -23
- package/package.json +32 -6
package/dist/cjs/index.d.ts
CHANGED
|
@@ -8,13 +8,10 @@ export interface GrainEvent {
|
|
|
8
8
|
properties?: Record<string, unknown>;
|
|
9
9
|
timestamp?: Date;
|
|
10
10
|
}
|
|
11
|
-
export interface
|
|
11
|
+
export interface EventPayload {
|
|
12
12
|
eventName: string;
|
|
13
|
-
eventTs: string;
|
|
14
13
|
userId: string;
|
|
15
14
|
properties: Record<string, unknown>;
|
|
16
|
-
eventDate: string;
|
|
17
|
-
insertId: string;
|
|
18
15
|
}
|
|
19
16
|
export type AuthStrategy = 'NONE' | 'SERVER_SIDE' | 'JWT';
|
|
20
17
|
export interface AuthProvider {
|
|
@@ -30,6 +27,7 @@ export interface GrainConfig {
|
|
|
30
27
|
flushInterval?: number;
|
|
31
28
|
retryAttempts?: number;
|
|
32
29
|
retryDelay?: number;
|
|
30
|
+
maxEventsPerRequest?: number;
|
|
33
31
|
debug?: boolean;
|
|
34
32
|
}
|
|
35
33
|
export interface SendEventOptions {
|
|
@@ -43,7 +41,6 @@ export declare class GrainAnalytics {
|
|
|
43
41
|
constructor(config: GrainConfig);
|
|
44
42
|
private validateConfig;
|
|
45
43
|
private log;
|
|
46
|
-
private generateInsertId;
|
|
47
44
|
private formatEvent;
|
|
48
45
|
private getAuthHeaders;
|
|
49
46
|
private delay;
|
|
@@ -65,6 +62,10 @@ export declare class GrainAnalytics {
|
|
|
65
62
|
* Manually flush all queued events
|
|
66
63
|
*/
|
|
67
64
|
flush(): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Split events array into chunks of specified size
|
|
67
|
+
*/
|
|
68
|
+
private chunkEvents;
|
|
68
69
|
/**
|
|
69
70
|
* Destroy the client and clean up resources
|
|
70
71
|
*/
|
package/dist/cjs/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,aAAa,GAAG,KAAK,CAAC;AAE1D,MAAM,WAAW,YAAY;IAC3B,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAUD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,EAAE,WAAW;IAmB/B,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,GAAG;IAMX,OAAO,CAAC,WAAW;YAQL,cAAc;YAsBd,KAAK;IAInB,OAAO,CAAC,gBAAgB;YAmBV,UAAU;YA+DV,oBAAoB;IAkClC,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,iBAAiB;IA0CzB;;OAEG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IACzG,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCzE;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAM9B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,IAAI,IAAI;CA8BhB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,cAAc,CAExE;AAGD,eAAe,cAAc,CAAC;AAG9B,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,KAAK,CAAC,EAAE;YACN,cAAc,EAAE,OAAO,cAAc,CAAC;YACtC,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;SACnD,CAAC;KACH;CACF"}
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -8,13 +8,10 @@ export interface GrainEvent {
|
|
|
8
8
|
properties?: Record<string, unknown>;
|
|
9
9
|
timestamp?: Date;
|
|
10
10
|
}
|
|
11
|
-
export interface
|
|
11
|
+
export interface EventPayload {
|
|
12
12
|
eventName: string;
|
|
13
|
-
eventTs: string;
|
|
14
13
|
userId: string;
|
|
15
14
|
properties: Record<string, unknown>;
|
|
16
|
-
eventDate: string;
|
|
17
|
-
insertId: string;
|
|
18
15
|
}
|
|
19
16
|
export type AuthStrategy = 'NONE' | 'SERVER_SIDE' | 'JWT';
|
|
20
17
|
export interface AuthProvider {
|
|
@@ -30,6 +27,7 @@ export interface GrainConfig {
|
|
|
30
27
|
flushInterval?: number;
|
|
31
28
|
retryAttempts?: number;
|
|
32
29
|
retryDelay?: number;
|
|
30
|
+
maxEventsPerRequest?: number;
|
|
33
31
|
debug?: boolean;
|
|
34
32
|
}
|
|
35
33
|
export interface SendEventOptions {
|
|
@@ -43,7 +41,6 @@ export declare class GrainAnalytics {
|
|
|
43
41
|
constructor(config: GrainConfig);
|
|
44
42
|
private validateConfig;
|
|
45
43
|
private log;
|
|
46
|
-
private generateInsertId;
|
|
47
44
|
private formatEvent;
|
|
48
45
|
private getAuthHeaders;
|
|
49
46
|
private delay;
|
|
@@ -65,6 +62,10 @@ export declare class GrainAnalytics {
|
|
|
65
62
|
* Manually flush all queued events
|
|
66
63
|
*/
|
|
67
64
|
flush(): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Split events array into chunks of specified size
|
|
67
|
+
*/
|
|
68
|
+
private chunkEvents;
|
|
68
69
|
/**
|
|
69
70
|
* Destroy the client and clean up resources
|
|
70
71
|
*/
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,aAAa,GAAG,KAAK,CAAC;AAE1D,MAAM,WAAW,YAAY;IAC3B,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAUD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,EAAE,WAAW;IAmB/B,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,GAAG;IAMX,OAAO,CAAC,WAAW;YAQL,cAAc;YAsBd,KAAK;IAInB,OAAO,CAAC,gBAAgB;YAmBV,UAAU;YA+DV,oBAAoB;IAkClC,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,iBAAiB;IA0CzB;;OAEG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IACzG,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCzE;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAM9B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,IAAI,IAAI;CA8BhB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,cAAc,CAExE;AAGD,eAAe,cAAc,CAAC;AAG9B,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,KAAK,CAAC,EAAE;YACN,cAAc,EAAE,OAAO,cAAc,CAAC;YACtC,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;SACnD,CAAC;KACH;CACF"}
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,13 +8,10 @@ export interface GrainEvent {
|
|
|
8
8
|
properties?: Record<string, unknown>;
|
|
9
9
|
timestamp?: Date;
|
|
10
10
|
}
|
|
11
|
-
export interface
|
|
11
|
+
export interface EventPayload {
|
|
12
12
|
eventName: string;
|
|
13
|
-
eventTs: string;
|
|
14
13
|
userId: string;
|
|
15
14
|
properties: Record<string, unknown>;
|
|
16
|
-
eventDate: string;
|
|
17
|
-
insertId: string;
|
|
18
15
|
}
|
|
19
16
|
export type AuthStrategy = 'NONE' | 'SERVER_SIDE' | 'JWT';
|
|
20
17
|
export interface AuthProvider {
|
|
@@ -30,6 +27,7 @@ export interface GrainConfig {
|
|
|
30
27
|
flushInterval?: number;
|
|
31
28
|
retryAttempts?: number;
|
|
32
29
|
retryDelay?: number;
|
|
30
|
+
maxEventsPerRequest?: number;
|
|
33
31
|
debug?: boolean;
|
|
34
32
|
}
|
|
35
33
|
export interface SendEventOptions {
|
|
@@ -43,7 +41,6 @@ export declare class GrainAnalytics {
|
|
|
43
41
|
constructor(config: GrainConfig);
|
|
44
42
|
private validateConfig;
|
|
45
43
|
private log;
|
|
46
|
-
private generateInsertId;
|
|
47
44
|
private formatEvent;
|
|
48
45
|
private getAuthHeaders;
|
|
49
46
|
private delay;
|
|
@@ -65,6 +62,10 @@ export declare class GrainAnalytics {
|
|
|
65
62
|
* Manually flush all queued events
|
|
66
63
|
*/
|
|
67
64
|
flush(): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Split events array into chunks of specified size
|
|
67
|
+
*/
|
|
68
|
+
private chunkEvents;
|
|
68
69
|
/**
|
|
69
70
|
* Destroy the client and clean up resources
|
|
70
71
|
*/
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,aAAa,GAAG,KAAK,CAAC;AAE1D,MAAM,WAAW,YAAY;IAC3B,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CACtC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAUD,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,EAAE,WAAW;IAmB/B,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,GAAG;IAMX,OAAO,CAAC,WAAW;YAQL,cAAc;YAsBd,KAAK;IAInB,OAAO,CAAC,gBAAgB;YAmBV,UAAU;YA+DV,oBAAoB;IAkClC,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,iBAAiB;IA0CzB;;OAEG;IACG,KAAK,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IACzG,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCzE;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;IAM9B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B;;OAEG;IACH,OAAO,CAAC,WAAW;IAQnB;;OAEG;IACH,OAAO,IAAI,IAAI;CA8BhB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,cAAc,CAExE;AAGD,eAAe,cAAc,CAAC;AAG9B,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,KAAK,CAAC,EAAE;YACN,cAAc,EAAE,OAAO,cAAc,CAAC;YACtC,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;SACnD,CAAC;KACH;CACF"}
|
package/dist/index.global.dev.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* Grain Analytics Web SDK v1.0
|
|
1
|
+
/* Grain Analytics Web SDK v1.1.0 | MIT License | Development Build */
|
|
2
2
|
"use strict";
|
|
3
3
|
var Grain = (() => {
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
@@ -40,6 +40,8 @@ var Grain = (() => {
|
|
|
40
40
|
retryAttempts: 3,
|
|
41
41
|
retryDelay: 1e3,
|
|
42
42
|
// 1 second
|
|
43
|
+
maxEventsPerRequest: 160,
|
|
44
|
+
// Maximum events per API request
|
|
43
45
|
debug: false,
|
|
44
46
|
...config,
|
|
45
47
|
tenantId: config.tenantId
|
|
@@ -64,20 +66,11 @@ var Grain = (() => {
|
|
|
64
66
|
console.log("[Grain Analytics]", ...args);
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
|
-
generateInsertId() {
|
|
68
|
-
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
69
|
-
}
|
|
70
69
|
formatEvent(event) {
|
|
71
|
-
const timestamp = event.timestamp || /* @__PURE__ */ new Date();
|
|
72
|
-
const eventTs = timestamp.toISOString();
|
|
73
|
-
const eventDate = timestamp.toISOString().split("T")[0];
|
|
74
70
|
return {
|
|
75
71
|
eventName: event.eventName,
|
|
76
|
-
eventTs,
|
|
77
72
|
userId: event.userId || "anonymous",
|
|
78
|
-
properties: event.properties || {}
|
|
79
|
-
eventDate,
|
|
80
|
-
insertId: this.generateInsertId()
|
|
73
|
+
properties: event.properties || {}
|
|
81
74
|
};
|
|
82
75
|
}
|
|
83
76
|
async getAuthHeaders() {
|
|
@@ -104,9 +97,14 @@ var Grain = (() => {
|
|
|
104
97
|
}
|
|
105
98
|
isRetriableError(error) {
|
|
106
99
|
if (error instanceof Error) {
|
|
107
|
-
|
|
100
|
+
const message = error.message.toLowerCase();
|
|
101
|
+
if (message.includes("fetch failed"))
|
|
102
|
+
return true;
|
|
103
|
+
if (message === "network error")
|
|
108
104
|
return true;
|
|
109
|
-
if (
|
|
105
|
+
if (message.includes("timeout"))
|
|
106
|
+
return true;
|
|
107
|
+
if (message.includes("connection"))
|
|
110
108
|
return true;
|
|
111
109
|
}
|
|
112
110
|
if (typeof error === "object" && error !== null && "status" in error) {
|
|
@@ -207,18 +205,26 @@ var Grain = (() => {
|
|
|
207
205
|
return;
|
|
208
206
|
const handleBeforeUnload = () => {
|
|
209
207
|
if (this.eventQueue.length > 0) {
|
|
210
|
-
|
|
211
|
-
});
|
|
208
|
+
const eventsToSend = [...this.eventQueue];
|
|
212
209
|
this.eventQueue = [];
|
|
210
|
+
const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
|
|
211
|
+
if (chunks.length > 0) {
|
|
212
|
+
this.sendEventsWithBeacon(chunks[0]).catch(() => {
|
|
213
|
+
});
|
|
214
|
+
}
|
|
213
215
|
}
|
|
214
216
|
};
|
|
215
217
|
window.addEventListener("beforeunload", handleBeforeUnload);
|
|
216
218
|
window.addEventListener("pagehide", handleBeforeUnload);
|
|
217
219
|
document.addEventListener("visibilitychange", () => {
|
|
218
220
|
if (document.visibilityState === "hidden" && this.eventQueue.length > 0) {
|
|
219
|
-
|
|
220
|
-
});
|
|
221
|
+
const eventsToSend = [...this.eventQueue];
|
|
221
222
|
this.eventQueue = [];
|
|
223
|
+
const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
|
|
224
|
+
if (chunks.length > 0) {
|
|
225
|
+
this.sendEventsWithBeacon(chunks[0]).catch(() => {
|
|
226
|
+
});
|
|
227
|
+
}
|
|
222
228
|
}
|
|
223
229
|
});
|
|
224
230
|
}
|
|
@@ -259,7 +265,20 @@ var Grain = (() => {
|
|
|
259
265
|
return;
|
|
260
266
|
const eventsToSend = [...this.eventQueue];
|
|
261
267
|
this.eventQueue = [];
|
|
262
|
-
|
|
268
|
+
const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
|
|
269
|
+
for (const chunk of chunks) {
|
|
270
|
+
await this.sendEvents(chunk);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Split events array into chunks of specified size
|
|
275
|
+
*/
|
|
276
|
+
chunkEvents(events, chunkSize) {
|
|
277
|
+
const chunks = [];
|
|
278
|
+
for (let i = 0; i < events.length; i += chunkSize) {
|
|
279
|
+
chunks.push(events.slice(i, i + chunkSize));
|
|
280
|
+
}
|
|
281
|
+
return chunks;
|
|
263
282
|
}
|
|
264
283
|
/**
|
|
265
284
|
* Destroy the client and clean up resources
|
|
@@ -271,9 +290,17 @@ var Grain = (() => {
|
|
|
271
290
|
this.flushTimer = null;
|
|
272
291
|
}
|
|
273
292
|
if (this.eventQueue.length > 0) {
|
|
274
|
-
|
|
275
|
-
});
|
|
293
|
+
const eventsToSend = [...this.eventQueue];
|
|
276
294
|
this.eventQueue = [];
|
|
295
|
+
const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
|
|
296
|
+
if (chunks.length > 0) {
|
|
297
|
+
this.sendEventsWithBeacon(chunks[0]).catch(() => {
|
|
298
|
+
});
|
|
299
|
+
for (let i = 1; i < chunks.length; i++) {
|
|
300
|
+
this.sendEventsWithBeacon(chunks[i]).catch(() => {
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
277
304
|
}
|
|
278
305
|
}
|
|
279
306
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Grain Analytics Web SDK\n * A lightweight, dependency-free TypeScript SDK for sending analytics events to Grain's REST API\n */\n\nexport interface GrainEvent {\n eventName: string;\n userId?: string;\n properties?: Record<string, unknown>;\n timestamp?: Date;\n}\n\nexport interface EventRow {\n eventName: string;\n eventTs: string;\n userId: string;\n properties: Record<string, unknown>;\n eventDate: string;\n insertId: string;\n}\n\nexport type AuthStrategy = 'NONE' | 'SERVER_SIDE' | 'JWT';\n\nexport interface AuthProvider {\n getToken(): Promise<string> | string;\n}\n\nexport interface GrainConfig {\n tenantId: string;\n apiUrl?: string;\n authStrategy?: AuthStrategy;\n secretKey?: string; // For SERVER_SIDE auth\n authProvider?: AuthProvider; // For JWT auth\n batchSize?: number;\n flushInterval?: number; // milliseconds\n retryAttempts?: number;\n retryDelay?: number; // milliseconds\n debug?: boolean;\n}\n\nexport interface SendEventOptions {\n flush?: boolean; // Force immediate send\n}\n\n/**\n * Main Grain Analytics client\n */\ntype RequiredConfig = Required<Omit<GrainConfig, 'secretKey' | 'authProvider'>> & {\n secretKey?: string;\n authProvider?: AuthProvider;\n};\n\nexport class GrainAnalytics {\n private config: RequiredConfig;\n private eventQueue: EventRow[] = [];\n private flushTimer: number | null = null;\n private isDestroyed = false;\n\n constructor(config: GrainConfig) {\n this.config = {\n apiUrl: 'https://api.grainql.com',\n authStrategy: 'NONE',\n batchSize: 50,\n flushInterval: 5000, // 5 seconds\n retryAttempts: 3,\n retryDelay: 1000, // 1 second\n debug: false,\n ...config,\n tenantId: config.tenantId,\n };\n\n this.validateConfig();\n this.setupBeforeUnload();\n this.startFlushTimer();\n }\n\n private validateConfig(): void {\n if (!this.config.tenantId) {\n throw new Error('Grain Analytics: tenantId is required');\n }\n\n if (this.config.authStrategy === 'SERVER_SIDE' && !this.config.secretKey) {\n throw new Error('Grain Analytics: secretKey is required for SERVER_SIDE auth strategy');\n }\n\n if (this.config.authStrategy === 'JWT' && !this.config.authProvider) {\n throw new Error('Grain Analytics: authProvider is required for JWT auth strategy');\n }\n }\n\n private log(...args: unknown[]): void {\n if (this.config.debug) {\n console.log('[Grain Analytics]', ...args);\n }\n }\n\n private generateInsertId(): string {\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }\n\n private formatEvent(event: GrainEvent): EventRow {\n const timestamp = event.timestamp || new Date();\n const eventTs = timestamp.toISOString();\n const eventDate = timestamp.toISOString().split('T')[0];\n\n return {\n eventName: event.eventName,\n eventTs,\n userId: event.userId || 'anonymous',\n properties: event.properties || {},\n eventDate,\n insertId: this.generateInsertId(),\n };\n }\n\n private async getAuthHeaders(): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n switch (this.config.authStrategy) {\n case 'NONE':\n break;\n case 'SERVER_SIDE':\n headers['Authorization'] = `Chase ${this.config.secretKey}`;\n break;\n case 'JWT':\n if (this.config.authProvider) {\n const token = await this.config.authProvider.getToken();\n headers['Authorization'] = `Bearer ${token}`;\n }\n break;\n }\n\n return headers;\n }\n\n private async delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n private isRetriableError(error: unknown): boolean {\n if (error instanceof Error) {\n // Check for network errors or server errors\n if (error.message.includes('fetch')) return true;\n if (error.message.includes('network')) return true;\n }\n \n // Check for HTTP status codes that are retriable\n if (typeof error === 'object' && error !== null && 'status' in error) {\n const status = (error as { status: number }).status;\n return status >= 500 || status === 429; // Server errors or rate limiting\n }\n \n return false;\n }\n\n private async sendEvents(events: EventRow[]): Promise<void> {\n if (events.length === 0) return;\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}`;\n\n this.log(`Sending ${events.length} events to ${url} (attempt ${attempt + 1})`);\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ events }),\n });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}`;\n try {\n const errorBody = await response.json();\n if (errorBody?.message) {\n errorMessage = errorBody.message;\n }\n } catch {\n const errorText = await response.text();\n if (errorText) {\n errorMessage = errorText;\n }\n }\n \n const error = new Error(`Failed to send events: ${errorMessage}`) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n this.log(`Successfully sent ${events.length} events`);\n return; // Success, exit retry loop\n \n } catch (error) {\n lastError = error;\n \n if (attempt === this.config.retryAttempts) {\n // Last attempt, don't retry\n break;\n }\n \n if (!this.isRetriableError(error)) {\n // Non-retriable error, don't retry\n break;\n }\n \n const delayMs = this.config.retryDelay * Math.pow(2, attempt); // Exponential backoff\n this.log(`Retrying in ${delayMs}ms after error:`, error);\n await this.delay(delayMs);\n }\n }\n\n console.error('[Grain Analytics] Failed to send events after all retries:', lastError);\n throw lastError;\n }\n\n private async sendEventsWithBeacon(events: EventRow[]): Promise<void> {\n if (events.length === 0) return;\n\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}`;\n\n const body = JSON.stringify({ events });\n\n // Try beacon API first (more reliable for page unload)\n if (typeof navigator !== 'undefined' && 'sendBeacon' in navigator) {\n const blob = new Blob([body], { type: 'application/json' });\n const success = navigator.sendBeacon(url, blob);\n \n if (success) {\n this.log(`Successfully sent ${events.length} events via beacon`);\n return;\n }\n }\n\n // Fallback to fetch with keepalive\n await fetch(url, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n });\n\n this.log(`Successfully sent ${events.length} events via fetch (keepalive)`);\n } catch (error) {\n console.error('[Grain Analytics] Failed to send events via beacon:', error);\n }\n }\n\n private startFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n\n this.flushTimer = window.setInterval(() => {\n if (this.eventQueue.length > 0) {\n this.flush().catch((error) => {\n console.error('[Grain Analytics] Auto-flush failed:', error);\n });\n }\n }, this.config.flushInterval);\n }\n\n private setupBeforeUnload(): void {\n if (typeof window === 'undefined') return;\n\n const handleBeforeUnload = () => {\n if (this.eventQueue.length > 0) {\n // Use beacon API for reliable delivery during page unload\n this.sendEventsWithBeacon([...this.eventQueue]).catch(() => {\n // Silently fail - page is unloading\n });\n this.eventQueue = [];\n }\n };\n\n // Handle page unload\n window.addEventListener('beforeunload', handleBeforeUnload);\n window.addEventListener('pagehide', handleBeforeUnload);\n \n // Handle visibility change (page hidden)\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden' && this.eventQueue.length > 0) {\n this.sendEventsWithBeacon([...this.eventQueue]).catch(() => {\n // Silently fail\n });\n this.eventQueue = [];\n }\n });\n }\n\n /**\n * Track an analytics event\n */\n async track(eventName: string, properties?: Record<string, unknown>, options?: SendEventOptions): Promise<void>;\n async track(event: GrainEvent, options?: SendEventOptions): Promise<void>;\n async track(\n eventOrName: string | GrainEvent,\n propertiesOrOptions?: Record<string, unknown> | SendEventOptions,\n options?: SendEventOptions\n ): Promise<void> {\n if (this.isDestroyed) {\n throw new Error('Grain Analytics: Client has been destroyed');\n }\n\n let event: GrainEvent;\n let opts: SendEventOptions = {};\n\n if (typeof eventOrName === 'string') {\n event = {\n eventName: eventOrName,\n properties: propertiesOrOptions as Record<string, unknown>,\n };\n opts = options || {};\n } else {\n event = eventOrName;\n opts = propertiesOrOptions as SendEventOptions || {};\n }\n\n const formattedEvent = this.formatEvent(event);\n this.eventQueue.push(formattedEvent);\n\n this.log(`Queued event: ${event.eventName}`, event.properties);\n\n // Check if we should flush immediately\n if (opts.flush || this.eventQueue.length >= this.config.batchSize) {\n await this.flush();\n }\n }\n\n /**\n * Identify a user (sets userId for subsequent events)\n */\n identify(userId: string): void {\n // Store userId for future events - this would typically be handled\n // by the application layer, but we can provide a helper\n this.log(`Identified user: ${userId}`);\n }\n\n /**\n * Manually flush all queued events\n */\n async flush(): Promise<void> {\n if (this.eventQueue.length === 0) return;\n\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n\n await this.sendEvents(eventsToSend);\n }\n\n /**\n * Destroy the client and clean up resources\n */\n destroy(): void {\n this.isDestroyed = true;\n \n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Send any remaining events\n if (this.eventQueue.length > 0) {\n this.sendEventsWithBeacon([...this.eventQueue]).catch(() => {\n // Silently fail during cleanup\n });\n this.eventQueue = [];\n }\n }\n}\n\n/**\n * Create a new Grain Analytics client\n */\nexport function createGrainAnalytics(config: GrainConfig): GrainAnalytics {\n return new GrainAnalytics(config);\n}\n\n// Default export for convenience\nexport default GrainAnalytics;\n\n// Global interface for IIFE build\ndeclare global {\n interface Window {\n Grain?: {\n GrainAnalytics: typeof GrainAnalytics;\n createGrainAnalytics: typeof createGrainAnalytics;\n };\n }\n}\n\n// Auto-setup for IIFE build\nif (typeof window !== 'undefined') {\n window.Grain = {\n GrainAnalytics,\n createGrainAnalytics,\n };\n}"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
4
|
+
"sourcesContent": ["/**\n * Grain Analytics Web SDK\n * A lightweight, dependency-free TypeScript SDK for sending analytics events to Grain's REST API\n */\n\nexport interface GrainEvent {\n eventName: string;\n userId?: string;\n properties?: Record<string, unknown>;\n timestamp?: Date;\n}\n\nexport interface EventPayload {\n eventName: string;\n userId: string;\n properties: Record<string, unknown>;\n}\n\nexport type AuthStrategy = 'NONE' | 'SERVER_SIDE' | 'JWT';\n\nexport interface AuthProvider {\n getToken(): Promise<string> | string;\n}\n\nexport interface GrainConfig {\n tenantId: string;\n apiUrl?: string;\n authStrategy?: AuthStrategy;\n secretKey?: string; // For SERVER_SIDE auth\n authProvider?: AuthProvider; // For JWT auth\n batchSize?: number;\n flushInterval?: number; // milliseconds\n retryAttempts?: number;\n retryDelay?: number; // milliseconds\n maxEventsPerRequest?: number; // Maximum events to send in a single API request\n debug?: boolean;\n}\n\nexport interface SendEventOptions {\n flush?: boolean; // Force immediate send\n}\n\n/**\n * Main Grain Analytics client\n */\ntype RequiredConfig = Required<Omit<GrainConfig, 'secretKey' | 'authProvider'>> & {\n secretKey?: string;\n authProvider?: AuthProvider;\n};\n\nexport class GrainAnalytics {\n private config: RequiredConfig;\n private eventQueue: EventPayload[] = [];\n private flushTimer: number | null = null;\n private isDestroyed = false;\n\n constructor(config: GrainConfig) {\n this.config = {\n apiUrl: 'https://api.grainql.com',\n authStrategy: 'NONE',\n batchSize: 50,\n flushInterval: 5000, // 5 seconds\n retryAttempts: 3,\n retryDelay: 1000, // 1 second\n maxEventsPerRequest: 160, // Maximum events per API request\n debug: false,\n ...config,\n tenantId: config.tenantId,\n };\n\n this.validateConfig();\n this.setupBeforeUnload();\n this.startFlushTimer();\n }\n\n private validateConfig(): void {\n if (!this.config.tenantId) {\n throw new Error('Grain Analytics: tenantId is required');\n }\n\n if (this.config.authStrategy === 'SERVER_SIDE' && !this.config.secretKey) {\n throw new Error('Grain Analytics: secretKey is required for SERVER_SIDE auth strategy');\n }\n\n if (this.config.authStrategy === 'JWT' && !this.config.authProvider) {\n throw new Error('Grain Analytics: authProvider is required for JWT auth strategy');\n }\n }\n\n private log(...args: unknown[]): void {\n if (this.config.debug) {\n console.log('[Grain Analytics]', ...args);\n }\n }\n\n private formatEvent(event: GrainEvent): EventPayload {\n return {\n eventName: event.eventName,\n userId: event.userId || 'anonymous',\n properties: event.properties || {},\n };\n }\n\n private async getAuthHeaders(): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n switch (this.config.authStrategy) {\n case 'NONE':\n break;\n case 'SERVER_SIDE':\n headers['Authorization'] = `Chase ${this.config.secretKey}`;\n break;\n case 'JWT':\n if (this.config.authProvider) {\n const token = await this.config.authProvider.getToken();\n headers['Authorization'] = `Bearer ${token}`;\n }\n break;\n }\n\n return headers;\n }\n\n private async delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n private isRetriableError(error: unknown): boolean {\n if (error instanceof Error) {\n // Check for specific network or fetch errors\n const message = error.message.toLowerCase();\n if (message.includes('fetch failed')) return true;\n if (message === 'network error') return true; // Exact match to avoid \"Non-network error\"\n if (message.includes('timeout')) return true;\n if (message.includes('connection')) return true;\n }\n \n // Check for HTTP status codes that are retriable\n if (typeof error === 'object' && error !== null && 'status' in error) {\n const status = (error as { status: number }).status;\n return status >= 500 || status === 429; // Server errors or rate limiting\n }\n \n return false;\n }\n\n private async sendEvents(events: EventPayload[]): Promise<void> {\n if (events.length === 0) return;\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}`;\n\n this.log(`Sending ${events.length} events to ${url} (attempt ${attempt + 1})`);\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ events }),\n });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}`;\n try {\n const errorBody = await response.json();\n if (errorBody?.message) {\n errorMessage = errorBody.message;\n }\n } catch {\n const errorText = await response.text();\n if (errorText) {\n errorMessage = errorText;\n }\n }\n \n const error = new Error(`Failed to send events: ${errorMessage}`) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n this.log(`Successfully sent ${events.length} events`);\n return; // Success, exit retry loop\n \n } catch (error) {\n lastError = error;\n \n if (attempt === this.config.retryAttempts) {\n // Last attempt, don't retry\n break;\n }\n \n if (!this.isRetriableError(error)) {\n // Non-retriable error, don't retry\n break;\n }\n \n const delayMs = this.config.retryDelay * Math.pow(2, attempt); // Exponential backoff\n this.log(`Retrying in ${delayMs}ms after error:`, error);\n await this.delay(delayMs);\n }\n }\n\n console.error('[Grain Analytics] Failed to send events after all retries:', lastError);\n throw lastError;\n }\n\n private async sendEventsWithBeacon(events: EventPayload[]): Promise<void> {\n if (events.length === 0) return;\n\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}`;\n\n const body = JSON.stringify({ events });\n\n // Try beacon API first (more reliable for page unload)\n if (typeof navigator !== 'undefined' && 'sendBeacon' in navigator) {\n const blob = new Blob([body], { type: 'application/json' });\n const success = navigator.sendBeacon(url, blob);\n \n if (success) {\n this.log(`Successfully sent ${events.length} events via beacon`);\n return;\n }\n }\n\n // Fallback to fetch with keepalive\n await fetch(url, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n });\n\n this.log(`Successfully sent ${events.length} events via fetch (keepalive)`);\n } catch (error) {\n console.error('[Grain Analytics] Failed to send events via beacon:', error);\n }\n }\n\n private startFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n\n this.flushTimer = window.setInterval(() => {\n if (this.eventQueue.length > 0) {\n this.flush().catch((error) => {\n console.error('[Grain Analytics] Auto-flush failed:', error);\n });\n }\n }, this.config.flushInterval);\n }\n\n private setupBeforeUnload(): void {\n if (typeof window === 'undefined') return;\n\n const handleBeforeUnload = () => {\n if (this.eventQueue.length > 0) {\n // Use beacon API for reliable delivery during page unload\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n \n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send first chunk with beacon (most important for page unload)\n if (chunks.length > 0) {\n this.sendEventsWithBeacon(chunks[0]).catch(() => {\n // Silently fail - page is unloading\n });\n }\n }\n };\n\n // Handle page unload\n window.addEventListener('beforeunload', handleBeforeUnload);\n window.addEventListener('pagehide', handleBeforeUnload);\n \n // Handle visibility change (page hidden)\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden' && this.eventQueue.length > 0) {\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n \n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send first chunk with beacon (most important for page hidden)\n if (chunks.length > 0) {\n this.sendEventsWithBeacon(chunks[0]).catch(() => {\n // Silently fail\n });\n }\n }\n });\n }\n\n /**\n * Track an analytics event\n */\n async track(eventName: string, properties?: Record<string, unknown>, options?: SendEventOptions): Promise<void>;\n async track(event: GrainEvent, options?: SendEventOptions): Promise<void>;\n async track(\n eventOrName: string | GrainEvent,\n propertiesOrOptions?: Record<string, unknown> | SendEventOptions,\n options?: SendEventOptions\n ): Promise<void> {\n if (this.isDestroyed) {\n throw new Error('Grain Analytics: Client has been destroyed');\n }\n\n let event: GrainEvent;\n let opts: SendEventOptions = {};\n\n if (typeof eventOrName === 'string') {\n event = {\n eventName: eventOrName,\n properties: propertiesOrOptions as Record<string, unknown>,\n };\n opts = options || {};\n } else {\n event = eventOrName;\n opts = propertiesOrOptions as SendEventOptions || {};\n }\n\n const formattedEvent = this.formatEvent(event);\n this.eventQueue.push(formattedEvent);\n\n this.log(`Queued event: ${event.eventName}`, event.properties);\n\n // Check if we should flush immediately\n if (opts.flush || this.eventQueue.length >= this.config.batchSize) {\n await this.flush();\n }\n }\n\n /**\n * Identify a user (sets userId for subsequent events)\n */\n identify(userId: string): void {\n // Store userId for future events - this would typically be handled\n // by the application layer, but we can provide a helper\n this.log(`Identified user: ${userId}`);\n }\n\n /**\n * Manually flush all queued events\n */\n async flush(): Promise<void> {\n if (this.eventQueue.length === 0) return;\n\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n\n // Split events into chunks to respect maxEventsPerRequest limit\n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send all chunks sequentially to maintain order\n for (const chunk of chunks) {\n await this.sendEvents(chunk);\n }\n }\n\n /**\n * Split events array into chunks of specified size\n */\n private chunkEvents(events: EventPayload[], chunkSize: number): EventPayload[][] {\n const chunks: EventPayload[][] = [];\n for (let i = 0; i < events.length; i += chunkSize) {\n chunks.push(events.slice(i, i + chunkSize));\n }\n return chunks;\n }\n\n /**\n * Destroy the client and clean up resources\n */\n destroy(): void {\n this.isDestroyed = true;\n \n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Send any remaining events (in chunks if necessary)\n if (this.eventQueue.length > 0) {\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n \n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send first chunk with beacon (most important for page unload)\n if (chunks.length > 0) {\n this.sendEventsWithBeacon(chunks[0]).catch(() => {\n // Silently fail during cleanup\n });\n \n // If there are more chunks, try to send them with regular fetch\n for (let i = 1; i < chunks.length; i++) {\n this.sendEventsWithBeacon(chunks[i]).catch(() => {\n // Silently fail during cleanup\n });\n }\n }\n }\n }\n}\n\n/**\n * Create a new Grain Analytics client\n */\nexport function createGrainAnalytics(config: GrainConfig): GrainAnalytics {\n return new GrainAnalytics(config);\n}\n\n// Default export for convenience\nexport default GrainAnalytics;\n\n// Global interface for IIFE build\ndeclare global {\n interface Window {\n Grain?: {\n GrainAnalytics: typeof GrainAnalytics;\n createGrainAnalytics: typeof createGrainAnalytics;\n };\n }\n}\n\n// Auto-setup for IIFE build\nif (typeof window !== 'undefined') {\n window.Grain = {\n GrainAnalytics,\n createGrainAnalytics,\n };\n}"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/index.global.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/* Grain Analytics Web SDK v1.0
|
|
2
|
-
"use strict";var Grain=(()=>{var c=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var
|
|
1
|
+
/* Grain Analytics Web SDK v1.1.0 | MIT License */
|
|
2
|
+
"use strict";var Grain=(()=>{var c=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var y=(r,e)=>{for(var t in e)c(r,t,{get:e[t],enumerable:!0})},p=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of v(e))!g.call(r,i)&&i!==t&&c(r,i,{get:()=>e[i],enumerable:!(n=f(e,i))||n.enumerable});return r};var E=r=>p(c({},"__esModule",{value:!0}),r);var w={};y(w,{GrainAnalytics:()=>a,createGrainAnalytics:()=>d,default:()=>m});var a=class{constructor(e){this.eventQueue=[];this.flushTimer=null;this.isDestroyed=!1;this.config={apiUrl:"https://api.grainql.com",authStrategy:"NONE",batchSize:50,flushInterval:5e3,retryAttempts:3,retryDelay:1e3,maxEventsPerRequest:160,debug:!1,...e,tenantId:e.tenantId},this.validateConfig(),this.setupBeforeUnload(),this.startFlushTimer()}validateConfig(){if(!this.config.tenantId)throw new Error("Grain Analytics: tenantId is required");if(this.config.authStrategy==="SERVER_SIDE"&&!this.config.secretKey)throw new Error("Grain Analytics: secretKey is required for SERVER_SIDE auth strategy");if(this.config.authStrategy==="JWT"&&!this.config.authProvider)throw new Error("Grain Analytics: authProvider is required for JWT auth strategy")}log(...e){this.config.debug&&console.log("[Grain Analytics]",...e)}formatEvent(e){return{eventName:e.eventName,userId:e.userId||"anonymous",properties:e.properties||{}}}async getAuthHeaders(){let e={"Content-Type":"application/json"};switch(this.config.authStrategy){case"NONE":break;case"SERVER_SIDE":e.Authorization=`Chase ${this.config.secretKey}`;break;case"JWT":if(this.config.authProvider){let t=await this.config.authProvider.getToken();e.Authorization=`Bearer ${t}`}break}return e}async delay(e){return new Promise(t=>setTimeout(t,e))}isRetriableError(e){if(e instanceof Error){let t=e.message.toLowerCase();if(t.includes("fetch failed")||t==="network error"||t.includes("timeout")||t.includes("connection"))return!0}if(typeof e=="object"&&e!==null&&"status"in e){let t=e.status;return t>=500||t===429}return!1}async sendEvents(e){if(e.length===0)return;let t;for(let n=0;n<=this.config.retryAttempts;n++)try{let i=await this.getAuthHeaders(),s=`${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}`;this.log(`Sending ${e.length} events to ${s} (attempt ${n+1})`);let o=await fetch(s,{method:"POST",headers:i,body:JSON.stringify({events:e})});if(!o.ok){let h=`HTTP ${o.status}`;try{let u=await o.json();u?.message&&(h=u.message)}catch{let u=await o.text();u&&(h=u)}let l=new Error(`Failed to send events: ${h}`);throw l.status=o.status,l}this.log(`Successfully sent ${e.length} events`);return}catch(i){if(t=i,n===this.config.retryAttempts||!this.isRetriableError(i))break;let s=this.config.retryDelay*Math.pow(2,n);this.log(`Retrying in ${s}ms after error:`,i),await this.delay(s)}throw console.error("[Grain Analytics] Failed to send events after all retries:",t),t}async sendEventsWithBeacon(e){if(e.length!==0)try{let t=await this.getAuthHeaders(),n=`${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}`,i=JSON.stringify({events:e});if(typeof navigator<"u"&&"sendBeacon"in navigator){let s=new Blob([i],{type:"application/json"});if(navigator.sendBeacon(n,s)){this.log(`Successfully sent ${e.length} events via beacon`);return}}await fetch(n,{method:"POST",headers:t,body:i,keepalive:!0}),this.log(`Successfully sent ${e.length} events via fetch (keepalive)`)}catch(t){console.error("[Grain Analytics] Failed to send events via beacon:",t)}}startFlushTimer(){this.flushTimer&&clearInterval(this.flushTimer),this.flushTimer=window.setInterval(()=>{this.eventQueue.length>0&&this.flush().catch(e=>{console.error("[Grain Analytics] Auto-flush failed:",e)})},this.config.flushInterval)}setupBeforeUnload(){if(typeof window>"u")return;let e=()=>{if(this.eventQueue.length>0){let t=[...this.eventQueue];this.eventQueue=[];let n=this.chunkEvents(t,this.config.maxEventsPerRequest);n.length>0&&this.sendEventsWithBeacon(n[0]).catch(()=>{})}};window.addEventListener("beforeunload",e),window.addEventListener("pagehide",e),document.addEventListener("visibilitychange",()=>{if(document.visibilityState==="hidden"&&this.eventQueue.length>0){let t=[...this.eventQueue];this.eventQueue=[];let n=this.chunkEvents(t,this.config.maxEventsPerRequest);n.length>0&&this.sendEventsWithBeacon(n[0]).catch(()=>{})}})}async track(e,t,n){if(this.isDestroyed)throw new Error("Grain Analytics: Client has been destroyed");let i,s={};typeof e=="string"?(i={eventName:e,properties:t},s=n||{}):(i=e,s=t||{});let o=this.formatEvent(i);this.eventQueue.push(o),this.log(`Queued event: ${i.eventName}`,i.properties),(s.flush||this.eventQueue.length>=this.config.batchSize)&&await this.flush()}identify(e){this.log(`Identified user: ${e}`)}async flush(){if(this.eventQueue.length===0)return;let e=[...this.eventQueue];this.eventQueue=[];let t=this.chunkEvents(e,this.config.maxEventsPerRequest);for(let n of t)await this.sendEvents(n)}chunkEvents(e,t){let n=[];for(let i=0;i<e.length;i+=t)n.push(e.slice(i,i+t));return n}destroy(){if(this.isDestroyed=!0,this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),this.eventQueue.length>0){let e=[...this.eventQueue];this.eventQueue=[];let t=this.chunkEvents(e,this.config.maxEventsPerRequest);if(t.length>0){this.sendEventsWithBeacon(t[0]).catch(()=>{});for(let n=1;n<t.length;n++)this.sendEventsWithBeacon(t[n]).catch(()=>{})}}}};function d(r){return new a(r)}var m=a;typeof window<"u"&&(window.Grain={GrainAnalytics:a,createGrainAnalytics:d});return E(w);})();
|
|
3
3
|
//# sourceMappingURL=index.global.js.map
|
package/dist/index.global.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Grain Analytics Web SDK\n * A lightweight, dependency-free TypeScript SDK for sending analytics events to Grain's REST API\n */\n\nexport interface GrainEvent {\n eventName: string;\n userId?: string;\n properties?: Record<string, unknown>;\n timestamp?: Date;\n}\n\nexport interface EventRow {\n eventName: string;\n eventTs: string;\n userId: string;\n properties: Record<string, unknown>;\n eventDate: string;\n insertId: string;\n}\n\nexport type AuthStrategy = 'NONE' | 'SERVER_SIDE' | 'JWT';\n\nexport interface AuthProvider {\n getToken(): Promise<string> | string;\n}\n\nexport interface GrainConfig {\n tenantId: string;\n apiUrl?: string;\n authStrategy?: AuthStrategy;\n secretKey?: string; // For SERVER_SIDE auth\n authProvider?: AuthProvider; // For JWT auth\n batchSize?: number;\n flushInterval?: number; // milliseconds\n retryAttempts?: number;\n retryDelay?: number; // milliseconds\n debug?: boolean;\n}\n\nexport interface SendEventOptions {\n flush?: boolean; // Force immediate send\n}\n\n/**\n * Main Grain Analytics client\n */\ntype RequiredConfig = Required<Omit<GrainConfig, 'secretKey' | 'authProvider'>> & {\n secretKey?: string;\n authProvider?: AuthProvider;\n};\n\nexport class GrainAnalytics {\n private config: RequiredConfig;\n private eventQueue: EventRow[] = [];\n private flushTimer: number | null = null;\n private isDestroyed = false;\n\n constructor(config: GrainConfig) {\n this.config = {\n apiUrl: 'https://api.grainql.com',\n authStrategy: 'NONE',\n batchSize: 50,\n flushInterval: 5000, // 5 seconds\n retryAttempts: 3,\n retryDelay: 1000, // 1 second\n debug: false,\n ...config,\n tenantId: config.tenantId,\n };\n\n this.validateConfig();\n this.setupBeforeUnload();\n this.startFlushTimer();\n }\n\n private validateConfig(): void {\n if (!this.config.tenantId) {\n throw new Error('Grain Analytics: tenantId is required');\n }\n\n if (this.config.authStrategy === 'SERVER_SIDE' && !this.config.secretKey) {\n throw new Error('Grain Analytics: secretKey is required for SERVER_SIDE auth strategy');\n }\n\n if (this.config.authStrategy === 'JWT' && !this.config.authProvider) {\n throw new Error('Grain Analytics: authProvider is required for JWT auth strategy');\n }\n }\n\n private log(...args: unknown[]): void {\n if (this.config.debug) {\n console.log('[Grain Analytics]', ...args);\n }\n }\n\n private generateInsertId(): string {\n return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }\n\n private formatEvent(event: GrainEvent): EventRow {\n const timestamp = event.timestamp || new Date();\n const eventTs = timestamp.toISOString();\n const eventDate = timestamp.toISOString().split('T')[0];\n\n return {\n eventName: event.eventName,\n eventTs,\n userId: event.userId || 'anonymous',\n properties: event.properties || {},\n eventDate,\n insertId: this.generateInsertId(),\n };\n }\n\n private async getAuthHeaders(): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n switch (this.config.authStrategy) {\n case 'NONE':\n break;\n case 'SERVER_SIDE':\n headers['Authorization'] = `Chase ${this.config.secretKey}`;\n break;\n case 'JWT':\n if (this.config.authProvider) {\n const token = await this.config.authProvider.getToken();\n headers['Authorization'] = `Bearer ${token}`;\n }\n break;\n }\n\n return headers;\n }\n\n private async delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n private isRetriableError(error: unknown): boolean {\n if (error instanceof Error) {\n // Check for network errors or server errors\n if (error.message.includes('fetch')) return true;\n if (error.message.includes('network')) return true;\n }\n \n // Check for HTTP status codes that are retriable\n if (typeof error === 'object' && error !== null && 'status' in error) {\n const status = (error as { status: number }).status;\n return status >= 500 || status === 429; // Server errors or rate limiting\n }\n \n return false;\n }\n\n private async sendEvents(events: EventRow[]): Promise<void> {\n if (events.length === 0) return;\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}`;\n\n this.log(`Sending ${events.length} events to ${url} (attempt ${attempt + 1})`);\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ events }),\n });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}`;\n try {\n const errorBody = await response.json();\n if (errorBody?.message) {\n errorMessage = errorBody.message;\n }\n } catch {\n const errorText = await response.text();\n if (errorText) {\n errorMessage = errorText;\n }\n }\n \n const error = new Error(`Failed to send events: ${errorMessage}`) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n this.log(`Successfully sent ${events.length} events`);\n return; // Success, exit retry loop\n \n } catch (error) {\n lastError = error;\n \n if (attempt === this.config.retryAttempts) {\n // Last attempt, don't retry\n break;\n }\n \n if (!this.isRetriableError(error)) {\n // Non-retriable error, don't retry\n break;\n }\n \n const delayMs = this.config.retryDelay * Math.pow(2, attempt); // Exponential backoff\n this.log(`Retrying in ${delayMs}ms after error:`, error);\n await this.delay(delayMs);\n }\n }\n\n console.error('[Grain Analytics] Failed to send events after all retries:', lastError);\n throw lastError;\n }\n\n private async sendEventsWithBeacon(events: EventRow[]): Promise<void> {\n if (events.length === 0) return;\n\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}`;\n\n const body = JSON.stringify({ events });\n\n // Try beacon API first (more reliable for page unload)\n if (typeof navigator !== 'undefined' && 'sendBeacon' in navigator) {\n const blob = new Blob([body], { type: 'application/json' });\n const success = navigator.sendBeacon(url, blob);\n \n if (success) {\n this.log(`Successfully sent ${events.length} events via beacon`);\n return;\n }\n }\n\n // Fallback to fetch with keepalive\n await fetch(url, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n });\n\n this.log(`Successfully sent ${events.length} events via fetch (keepalive)`);\n } catch (error) {\n console.error('[Grain Analytics] Failed to send events via beacon:', error);\n }\n }\n\n private startFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n\n this.flushTimer = window.setInterval(() => {\n if (this.eventQueue.length > 0) {\n this.flush().catch((error) => {\n console.error('[Grain Analytics] Auto-flush failed:', error);\n });\n }\n }, this.config.flushInterval);\n }\n\n private setupBeforeUnload(): void {\n if (typeof window === 'undefined') return;\n\n const handleBeforeUnload = () => {\n if (this.eventQueue.length > 0) {\n // Use beacon API for reliable delivery during page unload\n this.sendEventsWithBeacon([...this.eventQueue]).catch(() => {\n // Silently fail - page is unloading\n });\n this.eventQueue = [];\n }\n };\n\n // Handle page unload\n window.addEventListener('beforeunload', handleBeforeUnload);\n window.addEventListener('pagehide', handleBeforeUnload);\n \n // Handle visibility change (page hidden)\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden' && this.eventQueue.length > 0) {\n this.sendEventsWithBeacon([...this.eventQueue]).catch(() => {\n // Silently fail\n });\n this.eventQueue = [];\n }\n });\n }\n\n /**\n * Track an analytics event\n */\n async track(eventName: string, properties?: Record<string, unknown>, options?: SendEventOptions): Promise<void>;\n async track(event: GrainEvent, options?: SendEventOptions): Promise<void>;\n async track(\n eventOrName: string | GrainEvent,\n propertiesOrOptions?: Record<string, unknown> | SendEventOptions,\n options?: SendEventOptions\n ): Promise<void> {\n if (this.isDestroyed) {\n throw new Error('Grain Analytics: Client has been destroyed');\n }\n\n let event: GrainEvent;\n let opts: SendEventOptions = {};\n\n if (typeof eventOrName === 'string') {\n event = {\n eventName: eventOrName,\n properties: propertiesOrOptions as Record<string, unknown>,\n };\n opts = options || {};\n } else {\n event = eventOrName;\n opts = propertiesOrOptions as SendEventOptions || {};\n }\n\n const formattedEvent = this.formatEvent(event);\n this.eventQueue.push(formattedEvent);\n\n this.log(`Queued event: ${event.eventName}`, event.properties);\n\n // Check if we should flush immediately\n if (opts.flush || this.eventQueue.length >= this.config.batchSize) {\n await this.flush();\n }\n }\n\n /**\n * Identify a user (sets userId for subsequent events)\n */\n identify(userId: string): void {\n // Store userId for future events - this would typically be handled\n // by the application layer, but we can provide a helper\n this.log(`Identified user: ${userId}`);\n }\n\n /**\n * Manually flush all queued events\n */\n async flush(): Promise<void> {\n if (this.eventQueue.length === 0) return;\n\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n\n await this.sendEvents(eventsToSend);\n }\n\n /**\n * Destroy the client and clean up resources\n */\n destroy(): void {\n this.isDestroyed = true;\n \n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Send any remaining events\n if (this.eventQueue.length > 0) {\n this.sendEventsWithBeacon([...this.eventQueue]).catch(() => {\n // Silently fail during cleanup\n });\n this.eventQueue = [];\n }\n }\n}\n\n/**\n * Create a new Grain Analytics client\n */\nexport function createGrainAnalytics(config: GrainConfig): GrainAnalytics {\n return new GrainAnalytics(config);\n}\n\n// Default export for convenience\nexport default GrainAnalytics;\n\n// Global interface for IIFE build\ndeclare global {\n interface Window {\n Grain?: {\n GrainAnalytics: typeof GrainAnalytics;\n createGrainAnalytics: typeof createGrainAnalytics;\n };\n }\n}\n\n// Auto-setup for IIFE build\nif (typeof window !== 'undefined') {\n window.Grain = {\n GrainAnalytics,\n createGrainAnalytics,\n };\n}"],
|
|
5
|
-
"mappings": ";ybAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,oBAAAE,EAAA,yBAAAC,EAAA,YAAAC,
|
|
6
|
-
"names": ["src_exports", "__export", "GrainAnalytics", "createGrainAnalytics", "src_default", "config", "args", "event", "
|
|
4
|
+
"sourcesContent": ["/**\n * Grain Analytics Web SDK\n * A lightweight, dependency-free TypeScript SDK for sending analytics events to Grain's REST API\n */\n\nexport interface GrainEvent {\n eventName: string;\n userId?: string;\n properties?: Record<string, unknown>;\n timestamp?: Date;\n}\n\nexport interface EventPayload {\n eventName: string;\n userId: string;\n properties: Record<string, unknown>;\n}\n\nexport type AuthStrategy = 'NONE' | 'SERVER_SIDE' | 'JWT';\n\nexport interface AuthProvider {\n getToken(): Promise<string> | string;\n}\n\nexport interface GrainConfig {\n tenantId: string;\n apiUrl?: string;\n authStrategy?: AuthStrategy;\n secretKey?: string; // For SERVER_SIDE auth\n authProvider?: AuthProvider; // For JWT auth\n batchSize?: number;\n flushInterval?: number; // milliseconds\n retryAttempts?: number;\n retryDelay?: number; // milliseconds\n maxEventsPerRequest?: number; // Maximum events to send in a single API request\n debug?: boolean;\n}\n\nexport interface SendEventOptions {\n flush?: boolean; // Force immediate send\n}\n\n/**\n * Main Grain Analytics client\n */\ntype RequiredConfig = Required<Omit<GrainConfig, 'secretKey' | 'authProvider'>> & {\n secretKey?: string;\n authProvider?: AuthProvider;\n};\n\nexport class GrainAnalytics {\n private config: RequiredConfig;\n private eventQueue: EventPayload[] = [];\n private flushTimer: number | null = null;\n private isDestroyed = false;\n\n constructor(config: GrainConfig) {\n this.config = {\n apiUrl: 'https://api.grainql.com',\n authStrategy: 'NONE',\n batchSize: 50,\n flushInterval: 5000, // 5 seconds\n retryAttempts: 3,\n retryDelay: 1000, // 1 second\n maxEventsPerRequest: 160, // Maximum events per API request\n debug: false,\n ...config,\n tenantId: config.tenantId,\n };\n\n this.validateConfig();\n this.setupBeforeUnload();\n this.startFlushTimer();\n }\n\n private validateConfig(): void {\n if (!this.config.tenantId) {\n throw new Error('Grain Analytics: tenantId is required');\n }\n\n if (this.config.authStrategy === 'SERVER_SIDE' && !this.config.secretKey) {\n throw new Error('Grain Analytics: secretKey is required for SERVER_SIDE auth strategy');\n }\n\n if (this.config.authStrategy === 'JWT' && !this.config.authProvider) {\n throw new Error('Grain Analytics: authProvider is required for JWT auth strategy');\n }\n }\n\n private log(...args: unknown[]): void {\n if (this.config.debug) {\n console.log('[Grain Analytics]', ...args);\n }\n }\n\n private formatEvent(event: GrainEvent): EventPayload {\n return {\n eventName: event.eventName,\n userId: event.userId || 'anonymous',\n properties: event.properties || {},\n };\n }\n\n private async getAuthHeaders(): Promise<Record<string, string>> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n\n switch (this.config.authStrategy) {\n case 'NONE':\n break;\n case 'SERVER_SIDE':\n headers['Authorization'] = `Chase ${this.config.secretKey}`;\n break;\n case 'JWT':\n if (this.config.authProvider) {\n const token = await this.config.authProvider.getToken();\n headers['Authorization'] = `Bearer ${token}`;\n }\n break;\n }\n\n return headers;\n }\n\n private async delay(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n private isRetriableError(error: unknown): boolean {\n if (error instanceof Error) {\n // Check for specific network or fetch errors\n const message = error.message.toLowerCase();\n if (message.includes('fetch failed')) return true;\n if (message === 'network error') return true; // Exact match to avoid \"Non-network error\"\n if (message.includes('timeout')) return true;\n if (message.includes('connection')) return true;\n }\n \n // Check for HTTP status codes that are retriable\n if (typeof error === 'object' && error !== null && 'status' in error) {\n const status = (error as { status: number }).status;\n return status >= 500 || status === 429; // Server errors or rate limiting\n }\n \n return false;\n }\n\n private async sendEvents(events: EventPayload[]): Promise<void> {\n if (events.length === 0) return;\n\n let lastError: unknown;\n\n for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}`;\n\n this.log(`Sending ${events.length} events to ${url} (attempt ${attempt + 1})`);\n\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify({ events }),\n });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}`;\n try {\n const errorBody = await response.json();\n if (errorBody?.message) {\n errorMessage = errorBody.message;\n }\n } catch {\n const errorText = await response.text();\n if (errorText) {\n errorMessage = errorText;\n }\n }\n \n const error = new Error(`Failed to send events: ${errorMessage}`) as Error & { status?: number };\n error.status = response.status;\n throw error;\n }\n\n this.log(`Successfully sent ${events.length} events`);\n return; // Success, exit retry loop\n \n } catch (error) {\n lastError = error;\n \n if (attempt === this.config.retryAttempts) {\n // Last attempt, don't retry\n break;\n }\n \n if (!this.isRetriableError(error)) {\n // Non-retriable error, don't retry\n break;\n }\n \n const delayMs = this.config.retryDelay * Math.pow(2, attempt); // Exponential backoff\n this.log(`Retrying in ${delayMs}ms after error:`, error);\n await this.delay(delayMs);\n }\n }\n\n console.error('[Grain Analytics] Failed to send events after all retries:', lastError);\n throw lastError;\n }\n\n private async sendEventsWithBeacon(events: EventPayload[]): Promise<void> {\n if (events.length === 0) return;\n\n try {\n const headers = await this.getAuthHeaders();\n const url = `${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}`;\n\n const body = JSON.stringify({ events });\n\n // Try beacon API first (more reliable for page unload)\n if (typeof navigator !== 'undefined' && 'sendBeacon' in navigator) {\n const blob = new Blob([body], { type: 'application/json' });\n const success = navigator.sendBeacon(url, blob);\n \n if (success) {\n this.log(`Successfully sent ${events.length} events via beacon`);\n return;\n }\n }\n\n // Fallback to fetch with keepalive\n await fetch(url, {\n method: 'POST',\n headers,\n body,\n keepalive: true,\n });\n\n this.log(`Successfully sent ${events.length} events via fetch (keepalive)`);\n } catch (error) {\n console.error('[Grain Analytics] Failed to send events via beacon:', error);\n }\n }\n\n private startFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n }\n\n this.flushTimer = window.setInterval(() => {\n if (this.eventQueue.length > 0) {\n this.flush().catch((error) => {\n console.error('[Grain Analytics] Auto-flush failed:', error);\n });\n }\n }, this.config.flushInterval);\n }\n\n private setupBeforeUnload(): void {\n if (typeof window === 'undefined') return;\n\n const handleBeforeUnload = () => {\n if (this.eventQueue.length > 0) {\n // Use beacon API for reliable delivery during page unload\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n \n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send first chunk with beacon (most important for page unload)\n if (chunks.length > 0) {\n this.sendEventsWithBeacon(chunks[0]).catch(() => {\n // Silently fail - page is unloading\n });\n }\n }\n };\n\n // Handle page unload\n window.addEventListener('beforeunload', handleBeforeUnload);\n window.addEventListener('pagehide', handleBeforeUnload);\n \n // Handle visibility change (page hidden)\n document.addEventListener('visibilitychange', () => {\n if (document.visibilityState === 'hidden' && this.eventQueue.length > 0) {\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n \n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send first chunk with beacon (most important for page hidden)\n if (chunks.length > 0) {\n this.sendEventsWithBeacon(chunks[0]).catch(() => {\n // Silently fail\n });\n }\n }\n });\n }\n\n /**\n * Track an analytics event\n */\n async track(eventName: string, properties?: Record<string, unknown>, options?: SendEventOptions): Promise<void>;\n async track(event: GrainEvent, options?: SendEventOptions): Promise<void>;\n async track(\n eventOrName: string | GrainEvent,\n propertiesOrOptions?: Record<string, unknown> | SendEventOptions,\n options?: SendEventOptions\n ): Promise<void> {\n if (this.isDestroyed) {\n throw new Error('Grain Analytics: Client has been destroyed');\n }\n\n let event: GrainEvent;\n let opts: SendEventOptions = {};\n\n if (typeof eventOrName === 'string') {\n event = {\n eventName: eventOrName,\n properties: propertiesOrOptions as Record<string, unknown>,\n };\n opts = options || {};\n } else {\n event = eventOrName;\n opts = propertiesOrOptions as SendEventOptions || {};\n }\n\n const formattedEvent = this.formatEvent(event);\n this.eventQueue.push(formattedEvent);\n\n this.log(`Queued event: ${event.eventName}`, event.properties);\n\n // Check if we should flush immediately\n if (opts.flush || this.eventQueue.length >= this.config.batchSize) {\n await this.flush();\n }\n }\n\n /**\n * Identify a user (sets userId for subsequent events)\n */\n identify(userId: string): void {\n // Store userId for future events - this would typically be handled\n // by the application layer, but we can provide a helper\n this.log(`Identified user: ${userId}`);\n }\n\n /**\n * Manually flush all queued events\n */\n async flush(): Promise<void> {\n if (this.eventQueue.length === 0) return;\n\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n\n // Split events into chunks to respect maxEventsPerRequest limit\n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send all chunks sequentially to maintain order\n for (const chunk of chunks) {\n await this.sendEvents(chunk);\n }\n }\n\n /**\n * Split events array into chunks of specified size\n */\n private chunkEvents(events: EventPayload[], chunkSize: number): EventPayload[][] {\n const chunks: EventPayload[][] = [];\n for (let i = 0; i < events.length; i += chunkSize) {\n chunks.push(events.slice(i, i + chunkSize));\n }\n return chunks;\n }\n\n /**\n * Destroy the client and clean up resources\n */\n destroy(): void {\n this.isDestroyed = true;\n \n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n\n // Send any remaining events (in chunks if necessary)\n if (this.eventQueue.length > 0) {\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n \n const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);\n \n // Send first chunk with beacon (most important for page unload)\n if (chunks.length > 0) {\n this.sendEventsWithBeacon(chunks[0]).catch(() => {\n // Silently fail during cleanup\n });\n \n // If there are more chunks, try to send them with regular fetch\n for (let i = 1; i < chunks.length; i++) {\n this.sendEventsWithBeacon(chunks[i]).catch(() => {\n // Silently fail during cleanup\n });\n }\n }\n }\n }\n}\n\n/**\n * Create a new Grain Analytics client\n */\nexport function createGrainAnalytics(config: GrainConfig): GrainAnalytics {\n return new GrainAnalytics(config);\n}\n\n// Default export for convenience\nexport default GrainAnalytics;\n\n// Global interface for IIFE build\ndeclare global {\n interface Window {\n Grain?: {\n GrainAnalytics: typeof GrainAnalytics;\n createGrainAnalytics: typeof createGrainAnalytics;\n };\n }\n}\n\n// Auto-setup for IIFE build\nif (typeof window !== 'undefined') {\n window.Grain = {\n GrainAnalytics,\n createGrainAnalytics,\n };\n}"],
|
|
5
|
+
"mappings": ";ybAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,oBAAAE,EAAA,yBAAAC,EAAA,YAAAC,IAkDO,IAAMF,EAAN,KAAqB,CAM1B,YAAYG,EAAqB,CAJjC,KAAQ,WAA6B,CAAC,EACtC,KAAQ,WAA4B,KACpC,KAAQ,YAAc,GAGpB,KAAK,OAAS,CACZ,OAAQ,0BACR,aAAc,OACd,UAAW,GACX,cAAe,IACf,cAAe,EACf,WAAY,IACZ,oBAAqB,IACrB,MAAO,GACP,GAAGA,EACH,SAAUA,EAAO,QACnB,EAEA,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,CACvB,CAEQ,gBAAuB,CAC7B,GAAI,CAAC,KAAK,OAAO,SACf,MAAM,IAAI,MAAM,uCAAuC,EAGzD,GAAI,KAAK,OAAO,eAAiB,eAAiB,CAAC,KAAK,OAAO,UAC7D,MAAM,IAAI,MAAM,sEAAsE,EAGxF,GAAI,KAAK,OAAO,eAAiB,OAAS,CAAC,KAAK,OAAO,aACrD,MAAM,IAAI,MAAM,iEAAiE,CAErF,CAEQ,OAAOC,EAAuB,CAChC,KAAK,OAAO,OACd,QAAQ,IAAI,oBAAqB,GAAGA,CAAI,CAE5C,CAEQ,YAAYC,EAAiC,CACnD,MAAO,CACL,UAAWA,EAAM,UACjB,OAAQA,EAAM,QAAU,YACxB,WAAYA,EAAM,YAAc,CAAC,CACnC,CACF,CAEA,MAAc,gBAAkD,CAC9D,IAAMC,EAAkC,CACtC,eAAgB,kBAClB,EAEA,OAAQ,KAAK,OAAO,aAAc,CAChC,IAAK,OACH,MACF,IAAK,cACHA,EAAQ,cAAmB,SAAS,KAAK,OAAO,SAAS,GACzD,MACF,IAAK,MACH,GAAI,KAAK,OAAO,aAAc,CAC5B,IAAMC,EAAQ,MAAM,KAAK,OAAO,aAAa,SAAS,EACtDD,EAAQ,cAAmB,UAAUC,CAAK,EAC5C,CACA,KACJ,CAEA,OAAOD,CACT,CAEA,MAAc,MAAME,EAA2B,CAC7C,OAAO,IAAI,QAAQC,GAAW,WAAWA,EAASD,CAAE,CAAC,CACvD,CAEQ,iBAAiBE,EAAyB,CAChD,GAAIA,aAAiB,MAAO,CAE1B,IAAMC,EAAUD,EAAM,QAAQ,YAAY,EAI1C,GAHIC,EAAQ,SAAS,cAAc,GAC/BA,IAAY,iBACZA,EAAQ,SAAS,SAAS,GAC1BA,EAAQ,SAAS,YAAY,EAAG,MAAO,EAC7C,CAGA,GAAI,OAAOD,GAAU,UAAYA,IAAU,MAAQ,WAAYA,EAAO,CACpE,IAAME,EAAUF,EAA6B,OAC7C,OAAOE,GAAU,KAAOA,IAAW,GACrC,CAEA,MAAO,EACT,CAEA,MAAc,WAAWC,EAAuC,CAC9D,GAAIA,EAAO,SAAW,EAAG,OAEzB,IAAIC,EAEJ,QAASC,EAAU,EAAGA,GAAW,KAAK,OAAO,cAAeA,IAC1D,GAAI,CACF,IAAMT,EAAU,MAAM,KAAK,eAAe,EACpCU,EAAM,GAAG,KAAK,OAAO,MAAM,cAAc,mBAAmB,KAAK,OAAO,QAAQ,CAAC,GAEvF,KAAK,IAAI,WAAWH,EAAO,MAAM,cAAcG,CAAG,aAAaD,EAAU,CAAC,GAAG,EAE7E,IAAME,EAAW,MAAM,MAAMD,EAAK,CAChC,OAAQ,OACR,QAAAV,EACA,KAAM,KAAK,UAAU,CAAE,OAAAO,CAAO,CAAC,CACjC,CAAC,EAED,GAAI,CAACI,EAAS,GAAI,CAChB,IAAIC,EAAe,QAAQD,EAAS,MAAM,GAC1C,GAAI,CACF,IAAME,EAAY,MAAMF,EAAS,KAAK,EAClCE,GAAW,UACbD,EAAeC,EAAU,QAE7B,MAAQ,CACN,IAAMC,EAAY,MAAMH,EAAS,KAAK,EAClCG,IACFF,EAAeE,EAEnB,CAEA,IAAMV,EAAQ,IAAI,MAAM,0BAA0BQ,CAAY,EAAE,EAChE,MAAAR,EAAM,OAASO,EAAS,OAClBP,CACR,CAEA,KAAK,IAAI,qBAAqBG,EAAO,MAAM,SAAS,EACpD,MAEF,OAASH,EAAO,CAQd,GAPAI,EAAYJ,EAERK,IAAY,KAAK,OAAO,eAKxB,CAAC,KAAK,iBAAiBL,CAAK,EAE9B,MAGF,IAAMW,EAAU,KAAK,OAAO,WAAa,KAAK,IAAI,EAAGN,CAAO,EAC5D,KAAK,IAAI,eAAeM,CAAO,kBAAmBX,CAAK,EACvD,MAAM,KAAK,MAAMW,CAAO,CAC1B,CAGF,cAAQ,MAAM,6DAA8DP,CAAS,EAC/EA,CACR,CAEA,MAAc,qBAAqBD,EAAuC,CACxE,GAAIA,EAAO,SAAW,EAEtB,GAAI,CACF,IAAMP,EAAU,MAAM,KAAK,eAAe,EACpCU,EAAM,GAAG,KAAK,OAAO,MAAM,cAAc,mBAAmB,KAAK,OAAO,QAAQ,CAAC,GAEjFM,EAAO,KAAK,UAAU,CAAE,OAAAT,CAAO,CAAC,EAGtC,GAAI,OAAO,UAAc,KAAe,eAAgB,UAAW,CACjE,IAAMU,EAAO,IAAI,KAAK,CAACD,CAAI,EAAG,CAAE,KAAM,kBAAmB,CAAC,EAG1D,GAFgB,UAAU,WAAWN,EAAKO,CAAI,EAEjC,CACX,KAAK,IAAI,qBAAqBV,EAAO,MAAM,oBAAoB,EAC/D,MACF,CACF,CAGA,MAAM,MAAMG,EAAK,CACf,OAAQ,OACR,QAAAV,EACA,KAAAgB,EACA,UAAW,EACb,CAAC,EAED,KAAK,IAAI,qBAAqBT,EAAO,MAAM,+BAA+B,CAC5E,OAASH,EAAO,CACd,QAAQ,MAAM,sDAAuDA,CAAK,CAC5E,CACF,CAEQ,iBAAwB,CAC1B,KAAK,YACP,cAAc,KAAK,UAAU,EAG/B,KAAK,WAAa,OAAO,YAAY,IAAM,CACrC,KAAK,WAAW,OAAS,GAC3B,KAAK,MAAM,EAAE,MAAOA,GAAU,CAC5B,QAAQ,MAAM,uCAAwCA,CAAK,CAC7D,CAAC,CAEL,EAAG,KAAK,OAAO,aAAa,CAC9B,CAEQ,mBAA0B,CAChC,GAAI,OAAO,OAAW,IAAa,OAEnC,IAAMc,EAAqB,IAAM,CAC/B,GAAI,KAAK,WAAW,OAAS,EAAG,CAE9B,IAAMC,EAAe,CAAC,GAAG,KAAK,UAAU,EACxC,KAAK,WAAa,CAAC,EAEnB,IAAMC,EAAS,KAAK,YAAYD,EAAc,KAAK,OAAO,mBAAmB,EAGzEC,EAAO,OAAS,GAClB,KAAK,qBAAqBA,EAAO,CAAC,CAAC,EAAE,MAAM,IAAM,CAEjD,CAAC,CAEL,CACF,EAGA,OAAO,iBAAiB,eAAgBF,CAAkB,EAC1D,OAAO,iBAAiB,WAAYA,CAAkB,EAGtD,SAAS,iBAAiB,mBAAoB,IAAM,CAClD,GAAI,SAAS,kBAAoB,UAAY,KAAK,WAAW,OAAS,EAAG,CACvE,IAAMC,EAAe,CAAC,GAAG,KAAK,UAAU,EACxC,KAAK,WAAa,CAAC,EAEnB,IAAMC,EAAS,KAAK,YAAYD,EAAc,KAAK,OAAO,mBAAmB,EAGzEC,EAAO,OAAS,GAClB,KAAK,qBAAqBA,EAAO,CAAC,CAAC,EAAE,MAAM,IAAM,CAEjD,CAAC,CAEL,CACF,CAAC,CACH,CAOA,MAAM,MACJC,EACAC,EACAC,EACe,CACf,GAAI,KAAK,YACP,MAAM,IAAI,MAAM,4CAA4C,EAG9D,IAAIxB,EACAyB,EAAyB,CAAC,EAE1B,OAAOH,GAAgB,UACzBtB,EAAQ,CACN,UAAWsB,EACX,WAAYC,CACd,EACAE,EAAOD,GAAW,CAAC,IAEnBxB,EAAQsB,EACRG,EAAOF,GAA2C,CAAC,GAGrD,IAAMG,EAAiB,KAAK,YAAY1B,CAAK,EAC7C,KAAK,WAAW,KAAK0B,CAAc,EAEnC,KAAK,IAAI,iBAAiB1B,EAAM,SAAS,GAAIA,EAAM,UAAU,GAGzDyB,EAAK,OAAS,KAAK,WAAW,QAAU,KAAK,OAAO,YACtD,MAAM,KAAK,MAAM,CAErB,CAKA,SAASE,EAAsB,CAG7B,KAAK,IAAI,oBAAoBA,CAAM,EAAE,CACvC,CAKA,MAAM,OAAuB,CAC3B,GAAI,KAAK,WAAW,SAAW,EAAG,OAElC,IAAMP,EAAe,CAAC,GAAG,KAAK,UAAU,EACxC,KAAK,WAAa,CAAC,EAGnB,IAAMC,EAAS,KAAK,YAAYD,EAAc,KAAK,OAAO,mBAAmB,EAG7E,QAAWQ,KAASP,EAClB,MAAM,KAAK,WAAWO,CAAK,CAE/B,CAKQ,YAAYpB,EAAwBqB,EAAqC,CAC/E,IAAMR,EAA2B,CAAC,EAClC,QAAS,EAAI,EAAG,EAAIb,EAAO,OAAQ,GAAKqB,EACtCR,EAAO,KAAKb,EAAO,MAAM,EAAG,EAAIqB,CAAS,CAAC,EAE5C,OAAOR,CACT,CAKA,SAAgB,CASd,GARA,KAAK,YAAc,GAEf,KAAK,aACP,cAAc,KAAK,UAAU,EAC7B,KAAK,WAAa,MAIhB,KAAK,WAAW,OAAS,EAAG,CAC9B,IAAMD,EAAe,CAAC,GAAG,KAAK,UAAU,EACxC,KAAK,WAAa,CAAC,EAEnB,IAAMC,EAAS,KAAK,YAAYD,EAAc,KAAK,OAAO,mBAAmB,EAG7E,GAAIC,EAAO,OAAS,EAAG,CACrB,KAAK,qBAAqBA,EAAO,CAAC,CAAC,EAAE,MAAM,IAAM,CAEjD,CAAC,EAGD,QAASS,EAAI,EAAGA,EAAIT,EAAO,OAAQS,IACjC,KAAK,qBAAqBT,EAAOS,CAAC,CAAC,EAAE,MAAM,IAAM,CAEjD,CAAC,CAEL,CACF,CACF,CACF,EAKO,SAASlC,EAAqBE,EAAqC,CACxE,OAAO,IAAIH,EAAeG,CAAM,CAClC,CAGA,IAAOD,EAAQF,EAaX,OAAO,OAAW,MACpB,OAAO,MAAQ,CACb,eAAAA,EACA,qBAAAC,CACF",
|
|
6
|
+
"names": ["src_exports", "__export", "GrainAnalytics", "createGrainAnalytics", "src_default", "config", "args", "event", "headers", "token", "ms", "resolve", "error", "message", "status", "events", "lastError", "attempt", "url", "response", "errorMessage", "errorBody", "errorText", "delayMs", "body", "blob", "handleBeforeUnload", "eventsToSend", "chunks", "eventOrName", "propertiesOrOptions", "options", "opts", "formattedEvent", "userId", "chunk", "chunkSize", "i"]
|
|
7
7
|
}
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ class GrainAnalytics {
|
|
|
18
18
|
flushInterval: 5000, // 5 seconds
|
|
19
19
|
retryAttempts: 3,
|
|
20
20
|
retryDelay: 1000, // 1 second
|
|
21
|
+
maxEventsPerRequest: 160, // Maximum events per API request
|
|
21
22
|
debug: false,
|
|
22
23
|
...config,
|
|
23
24
|
tenantId: config.tenantId,
|
|
@@ -42,20 +43,11 @@ class GrainAnalytics {
|
|
|
42
43
|
console.log('[Grain Analytics]', ...args);
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
|
-
generateInsertId() {
|
|
46
|
-
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
47
|
-
}
|
|
48
46
|
formatEvent(event) {
|
|
49
|
-
const timestamp = event.timestamp || new Date();
|
|
50
|
-
const eventTs = timestamp.toISOString();
|
|
51
|
-
const eventDate = timestamp.toISOString().split('T')[0];
|
|
52
47
|
return {
|
|
53
48
|
eventName: event.eventName,
|
|
54
|
-
eventTs,
|
|
55
49
|
userId: event.userId || 'anonymous',
|
|
56
50
|
properties: event.properties || {},
|
|
57
|
-
eventDate,
|
|
58
|
-
insertId: this.generateInsertId(),
|
|
59
51
|
};
|
|
60
52
|
}
|
|
61
53
|
async getAuthHeaders() {
|
|
@@ -82,10 +74,15 @@ class GrainAnalytics {
|
|
|
82
74
|
}
|
|
83
75
|
isRetriableError(error) {
|
|
84
76
|
if (error instanceof Error) {
|
|
85
|
-
// Check for network
|
|
86
|
-
|
|
77
|
+
// Check for specific network or fetch errors
|
|
78
|
+
const message = error.message.toLowerCase();
|
|
79
|
+
if (message.includes('fetch failed'))
|
|
80
|
+
return true;
|
|
81
|
+
if (message === 'network error')
|
|
82
|
+
return true; // Exact match to avoid "Non-network error"
|
|
83
|
+
if (message.includes('timeout'))
|
|
87
84
|
return true;
|
|
88
|
-
if (
|
|
85
|
+
if (message.includes('connection'))
|
|
89
86
|
return true;
|
|
90
87
|
}
|
|
91
88
|
// Check for HTTP status codes that are retriable
|
|
@@ -195,10 +192,15 @@ class GrainAnalytics {
|
|
|
195
192
|
const handleBeforeUnload = () => {
|
|
196
193
|
if (this.eventQueue.length > 0) {
|
|
197
194
|
// Use beacon API for reliable delivery during page unload
|
|
198
|
-
|
|
199
|
-
// Silently fail - page is unloading
|
|
200
|
-
});
|
|
195
|
+
const eventsToSend = [...this.eventQueue];
|
|
201
196
|
this.eventQueue = [];
|
|
197
|
+
const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
|
|
198
|
+
// Send first chunk with beacon (most important for page unload)
|
|
199
|
+
if (chunks.length > 0) {
|
|
200
|
+
this.sendEventsWithBeacon(chunks[0]).catch(() => {
|
|
201
|
+
// Silently fail - page is unloading
|
|
202
|
+
});
|
|
203
|
+
}
|
|
202
204
|
}
|
|
203
205
|
};
|
|
204
206
|
// Handle page unload
|
|
@@ -207,10 +209,15 @@ class GrainAnalytics {
|
|
|
207
209
|
// Handle visibility change (page hidden)
|
|
208
210
|
document.addEventListener('visibilitychange', () => {
|
|
209
211
|
if (document.visibilityState === 'hidden' && this.eventQueue.length > 0) {
|
|
210
|
-
|
|
211
|
-
// Silently fail
|
|
212
|
-
});
|
|
212
|
+
const eventsToSend = [...this.eventQueue];
|
|
213
213
|
this.eventQueue = [];
|
|
214
|
+
const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
|
|
215
|
+
// Send first chunk with beacon (most important for page hidden)
|
|
216
|
+
if (chunks.length > 0) {
|
|
217
|
+
this.sendEventsWithBeacon(chunks[0]).catch(() => {
|
|
218
|
+
// Silently fail
|
|
219
|
+
});
|
|
220
|
+
}
|
|
214
221
|
}
|
|
215
222
|
});
|
|
216
223
|
}
|
|
@@ -255,7 +262,22 @@ class GrainAnalytics {
|
|
|
255
262
|
return;
|
|
256
263
|
const eventsToSend = [...this.eventQueue];
|
|
257
264
|
this.eventQueue = [];
|
|
258
|
-
|
|
265
|
+
// Split events into chunks to respect maxEventsPerRequest limit
|
|
266
|
+
const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
|
|
267
|
+
// Send all chunks sequentially to maintain order
|
|
268
|
+
for (const chunk of chunks) {
|
|
269
|
+
await this.sendEvents(chunk);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Split events array into chunks of specified size
|
|
274
|
+
*/
|
|
275
|
+
chunkEvents(events, chunkSize) {
|
|
276
|
+
const chunks = [];
|
|
277
|
+
for (let i = 0; i < events.length; i += chunkSize) {
|
|
278
|
+
chunks.push(events.slice(i, i + chunkSize));
|
|
279
|
+
}
|
|
280
|
+
return chunks;
|
|
259
281
|
}
|
|
260
282
|
/**
|
|
261
283
|
* Destroy the client and clean up resources
|
|
@@ -266,12 +288,23 @@ class GrainAnalytics {
|
|
|
266
288
|
clearInterval(this.flushTimer);
|
|
267
289
|
this.flushTimer = null;
|
|
268
290
|
}
|
|
269
|
-
// Send any remaining events
|
|
291
|
+
// Send any remaining events (in chunks if necessary)
|
|
270
292
|
if (this.eventQueue.length > 0) {
|
|
271
|
-
|
|
272
|
-
// Silently fail during cleanup
|
|
273
|
-
});
|
|
293
|
+
const eventsToSend = [...this.eventQueue];
|
|
274
294
|
this.eventQueue = [];
|
|
295
|
+
const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
|
|
296
|
+
// Send first chunk with beacon (most important for page unload)
|
|
297
|
+
if (chunks.length > 0) {
|
|
298
|
+
this.sendEventsWithBeacon(chunks[0]).catch(() => {
|
|
299
|
+
// Silently fail during cleanup
|
|
300
|
+
});
|
|
301
|
+
// If there are more chunks, try to send them with regular fetch
|
|
302
|
+
for (let i = 1; i < chunks.length; i++) {
|
|
303
|
+
this.sendEventsWithBeacon(chunks[i]).catch(() => {
|
|
304
|
+
// Silently fail during cleanup
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
275
308
|
}
|
|
276
309
|
}
|
|
277
310
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -14,6 +14,7 @@ export class GrainAnalytics {
|
|
|
14
14
|
flushInterval: 5000, // 5 seconds
|
|
15
15
|
retryAttempts: 3,
|
|
16
16
|
retryDelay: 1000, // 1 second
|
|
17
|
+
maxEventsPerRequest: 160, // Maximum events per API request
|
|
17
18
|
debug: false,
|
|
18
19
|
...config,
|
|
19
20
|
tenantId: config.tenantId,
|
|
@@ -38,20 +39,11 @@ export class GrainAnalytics {
|
|
|
38
39
|
console.log('[Grain Analytics]', ...args);
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
|
-
generateInsertId() {
|
|
42
|
-
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
43
|
-
}
|
|
44
42
|
formatEvent(event) {
|
|
45
|
-
const timestamp = event.timestamp || new Date();
|
|
46
|
-
const eventTs = timestamp.toISOString();
|
|
47
|
-
const eventDate = timestamp.toISOString().split('T')[0];
|
|
48
43
|
return {
|
|
49
44
|
eventName: event.eventName,
|
|
50
|
-
eventTs,
|
|
51
45
|
userId: event.userId || 'anonymous',
|
|
52
46
|
properties: event.properties || {},
|
|
53
|
-
eventDate,
|
|
54
|
-
insertId: this.generateInsertId(),
|
|
55
47
|
};
|
|
56
48
|
}
|
|
57
49
|
async getAuthHeaders() {
|
|
@@ -78,10 +70,15 @@ export class GrainAnalytics {
|
|
|
78
70
|
}
|
|
79
71
|
isRetriableError(error) {
|
|
80
72
|
if (error instanceof Error) {
|
|
81
|
-
// Check for network
|
|
82
|
-
|
|
73
|
+
// Check for specific network or fetch errors
|
|
74
|
+
const message = error.message.toLowerCase();
|
|
75
|
+
if (message.includes('fetch failed'))
|
|
76
|
+
return true;
|
|
77
|
+
if (message === 'network error')
|
|
78
|
+
return true; // Exact match to avoid "Non-network error"
|
|
79
|
+
if (message.includes('timeout'))
|
|
83
80
|
return true;
|
|
84
|
-
if (
|
|
81
|
+
if (message.includes('connection'))
|
|
85
82
|
return true;
|
|
86
83
|
}
|
|
87
84
|
// Check for HTTP status codes that are retriable
|
|
@@ -191,10 +188,15 @@ export class GrainAnalytics {
|
|
|
191
188
|
const handleBeforeUnload = () => {
|
|
192
189
|
if (this.eventQueue.length > 0) {
|
|
193
190
|
// Use beacon API for reliable delivery during page unload
|
|
194
|
-
|
|
195
|
-
// Silently fail - page is unloading
|
|
196
|
-
});
|
|
191
|
+
const eventsToSend = [...this.eventQueue];
|
|
197
192
|
this.eventQueue = [];
|
|
193
|
+
const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
|
|
194
|
+
// Send first chunk with beacon (most important for page unload)
|
|
195
|
+
if (chunks.length > 0) {
|
|
196
|
+
this.sendEventsWithBeacon(chunks[0]).catch(() => {
|
|
197
|
+
// Silently fail - page is unloading
|
|
198
|
+
});
|
|
199
|
+
}
|
|
198
200
|
}
|
|
199
201
|
};
|
|
200
202
|
// Handle page unload
|
|
@@ -203,10 +205,15 @@ export class GrainAnalytics {
|
|
|
203
205
|
// Handle visibility change (page hidden)
|
|
204
206
|
document.addEventListener('visibilitychange', () => {
|
|
205
207
|
if (document.visibilityState === 'hidden' && this.eventQueue.length > 0) {
|
|
206
|
-
|
|
207
|
-
// Silently fail
|
|
208
|
-
});
|
|
208
|
+
const eventsToSend = [...this.eventQueue];
|
|
209
209
|
this.eventQueue = [];
|
|
210
|
+
const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
|
|
211
|
+
// Send first chunk with beacon (most important for page hidden)
|
|
212
|
+
if (chunks.length > 0) {
|
|
213
|
+
this.sendEventsWithBeacon(chunks[0]).catch(() => {
|
|
214
|
+
// Silently fail
|
|
215
|
+
});
|
|
216
|
+
}
|
|
210
217
|
}
|
|
211
218
|
});
|
|
212
219
|
}
|
|
@@ -251,7 +258,22 @@ export class GrainAnalytics {
|
|
|
251
258
|
return;
|
|
252
259
|
const eventsToSend = [...this.eventQueue];
|
|
253
260
|
this.eventQueue = [];
|
|
254
|
-
|
|
261
|
+
// Split events into chunks to respect maxEventsPerRequest limit
|
|
262
|
+
const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
|
|
263
|
+
// Send all chunks sequentially to maintain order
|
|
264
|
+
for (const chunk of chunks) {
|
|
265
|
+
await this.sendEvents(chunk);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Split events array into chunks of specified size
|
|
270
|
+
*/
|
|
271
|
+
chunkEvents(events, chunkSize) {
|
|
272
|
+
const chunks = [];
|
|
273
|
+
for (let i = 0; i < events.length; i += chunkSize) {
|
|
274
|
+
chunks.push(events.slice(i, i + chunkSize));
|
|
275
|
+
}
|
|
276
|
+
return chunks;
|
|
255
277
|
}
|
|
256
278
|
/**
|
|
257
279
|
* Destroy the client and clean up resources
|
|
@@ -262,12 +284,23 @@ export class GrainAnalytics {
|
|
|
262
284
|
clearInterval(this.flushTimer);
|
|
263
285
|
this.flushTimer = null;
|
|
264
286
|
}
|
|
265
|
-
// Send any remaining events
|
|
287
|
+
// Send any remaining events (in chunks if necessary)
|
|
266
288
|
if (this.eventQueue.length > 0) {
|
|
267
|
-
|
|
268
|
-
// Silently fail during cleanup
|
|
269
|
-
});
|
|
289
|
+
const eventsToSend = [...this.eventQueue];
|
|
270
290
|
this.eventQueue = [];
|
|
291
|
+
const chunks = this.chunkEvents(eventsToSend, this.config.maxEventsPerRequest);
|
|
292
|
+
// Send first chunk with beacon (most important for page unload)
|
|
293
|
+
if (chunks.length > 0) {
|
|
294
|
+
this.sendEventsWithBeacon(chunks[0]).catch(() => {
|
|
295
|
+
// Silently fail during cleanup
|
|
296
|
+
});
|
|
297
|
+
// If there are more chunks, try to send them with regular fetch
|
|
298
|
+
for (let i = 1; i < chunks.length; i++) {
|
|
299
|
+
this.sendEventsWithBeacon(chunks[i]).catch(() => {
|
|
300
|
+
// Silently fail during cleanup
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
271
304
|
}
|
|
272
305
|
}
|
|
273
306
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grainql/analytics-web",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Lightweight TypeScript SDK for sending analytics events to Grain's REST API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -22,7 +22,13 @@
|
|
|
22
22
|
"build:cjs": "tsc --module commonjs --target es2020 --outDir dist/cjs && mv dist/cjs/index.js dist/index.js",
|
|
23
23
|
"build:iife": "node scripts/build-iife.js",
|
|
24
24
|
"clean": "rm -rf dist",
|
|
25
|
-
"
|
|
25
|
+
"test": "jest",
|
|
26
|
+
"test:watch": "jest --watch",
|
|
27
|
+
"test:coverage": "jest --coverage",
|
|
28
|
+
"test:ci": "jest --ci --coverage --watchAll=false",
|
|
29
|
+
"prepublishOnly": "npm run clean && npm run test:ci && npm run build",
|
|
30
|
+
"size": "npm run build && node scripts/bundle-analysis.js",
|
|
31
|
+
"size:limit": "size-limit"
|
|
26
32
|
},
|
|
27
33
|
"keywords": [
|
|
28
34
|
"analytics",
|
|
@@ -34,15 +40,35 @@
|
|
|
34
40
|
],
|
|
35
41
|
"author": "Grain Analytics",
|
|
36
42
|
"license": "MIT",
|
|
37
|
-
"dependencies": {},
|
|
38
43
|
"devDependencies": {
|
|
39
|
-
"
|
|
40
|
-
"
|
|
44
|
+
"@jest/globals": "^30.0.5",
|
|
45
|
+
"@size-limit/preset-small-lib": "^11.2.0",
|
|
46
|
+
"@types/jest": "^30.0.0",
|
|
47
|
+
"@types/jsdom": "^21.1.7",
|
|
48
|
+
"esbuild": "^0.19.0",
|
|
49
|
+
"jest": "^30.0.5",
|
|
50
|
+
"jest-environment-jsdom": "^30.0.5",
|
|
51
|
+
"jsdom": "^26.1.0",
|
|
52
|
+
"size-limit": "^11.2.0",
|
|
53
|
+
"ts-jest": "^29.4.1",
|
|
54
|
+
"typescript": "^5.5.3"
|
|
41
55
|
},
|
|
42
56
|
"repository": {
|
|
43
57
|
"type": "git",
|
|
44
58
|
"url": "https://github.com/grain-analytics/web-sdk.git"
|
|
45
59
|
},
|
|
46
60
|
"homepage": "https://grainql.com",
|
|
47
|
-
"private": false
|
|
61
|
+
"private": false,
|
|
62
|
+
"size-limit": [
|
|
63
|
+
{
|
|
64
|
+
"path": "dist/index.global.js",
|
|
65
|
+
"limit": "6 KB",
|
|
66
|
+
"gzip": true
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"path": "dist/index.mjs",
|
|
70
|
+
"limit": "12 KB",
|
|
71
|
+
"gzip": false
|
|
72
|
+
}
|
|
73
|
+
]
|
|
48
74
|
}
|