@grainql/analytics-web 1.0.0 → 1.0.1
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/README.md +4 -4
- package/dist/index.global.dev.js +1 -1
- package/dist/index.global.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# @
|
|
1
|
+
# @grainql/analytics-web
|
|
2
2
|
|
|
3
3
|
A lightweight, dependency-free TypeScript SDK for sending analytics events to Grain's REST API with automatic batching, retry logic, and reliable event delivery.
|
|
4
4
|
|
|
@@ -15,7 +15,7 @@ A lightweight, dependency-free TypeScript SDK for sending analytics events to Gr
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npm install @
|
|
18
|
+
npm install @grainql/analytics-web
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Quick Start
|
|
@@ -23,7 +23,7 @@ npm install @grain-analytics/web
|
|
|
23
23
|
### Basic Usage (No Authentication)
|
|
24
24
|
|
|
25
25
|
```typescript
|
|
26
|
-
import { createGrainAnalytics } from '@
|
|
26
|
+
import { createGrainAnalytics } from '@grainql/analytics-web';
|
|
27
27
|
|
|
28
28
|
const grain = createGrainAnalytics({
|
|
29
29
|
tenantId: 'your-tenant-id',
|
|
@@ -170,7 +170,7 @@ The package provides multiple build formats:
|
|
|
170
170
|
### Browser Usage (Script Tag)
|
|
171
171
|
|
|
172
172
|
```html
|
|
173
|
-
<script src="https://unpkg.com/@
|
|
173
|
+
<script src="https://unpkg.com/@grainql/analytics-web/dist/index.global.js"></script>
|
|
174
174
|
<script>
|
|
175
175
|
const grain = Grain.createGrainAnalytics({
|
|
176
176
|
tenantId: 'your-tenant-id'
|
package/dist/index.global.dev.js
CHANGED
package/dist/index.global.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
/* Grain Analytics Web SDK v1.0.
|
|
1
|
+
/* Grain Analytics Web SDK v1.0.1 | MIT License */
|
|
2
2
|
"use strict";var Grain=(()=>{var c=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var g=Object.prototype.hasOwnProperty;var p=(s,e)=>{for(var t in e)c(s,t,{get:e[t],enumerable:!0})},y=(s,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of v(e))!g.call(s,n)&&n!==t&&c(s,n,{get:()=>e[n],enumerable:!(i=f(e,n))||i.enumerable});return s};var m=s=>y(c({},"__esModule",{value:!0}),s);var E={};p(E,{GrainAnalytics:()=>o,createGrainAnalytics:()=>l,default:()=>w});var o=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,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)}generateInsertId(){return`${Date.now()}-${Math.random().toString(36).substr(2,9)}`}formatEvent(e){let t=e.timestamp||new Date,i=t.toISOString(),n=t.toISOString().split("T")[0];return{eventName:e.eventName,eventTs:i,userId:e.userId||"anonymous",properties:e.properties||{},eventDate:n,insertId:this.generateInsertId()}}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&&(e.message.includes("fetch")||e.message.includes("network")))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 i=0;i<=this.config.retryAttempts;i++)try{let n=await this.getAuthHeaders(),r=`${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}`;this.log(`Sending ${e.length} events to ${r} (attempt ${i+1})`);let a=await fetch(r,{method:"POST",headers:n,body:JSON.stringify({events:e})});if(!a.ok){let h=`HTTP ${a.status}`;try{let u=await a.json();u?.message&&(h=u.message)}catch{let u=await a.text();u&&(h=u)}let d=new Error(`Failed to send events: ${h}`);throw d.status=a.status,d}this.log(`Successfully sent ${e.length} events`);return}catch(n){if(t=n,i===this.config.retryAttempts||!this.isRetriableError(n))break;let r=this.config.retryDelay*Math.pow(2,i);this.log(`Retrying in ${r}ms after error:`,n),await this.delay(r)}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(),i=`${this.config.apiUrl}/v1/events/${encodeURIComponent(this.config.tenantId)}`,n=JSON.stringify({events:e});if(typeof navigator<"u"&&"sendBeacon"in navigator){let r=new Blob([n],{type:"application/json"});if(navigator.sendBeacon(i,r)){this.log(`Successfully sent ${e.length} events via beacon`);return}}await fetch(i,{method:"POST",headers:t,body:n,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=()=>{this.eventQueue.length>0&&(this.sendEventsWithBeacon([...this.eventQueue]).catch(()=>{}),this.eventQueue=[])};window.addEventListener("beforeunload",e),window.addEventListener("pagehide",e),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.eventQueue.length>0&&(this.sendEventsWithBeacon([...this.eventQueue]).catch(()=>{}),this.eventQueue=[])})}async track(e,t,i){if(this.isDestroyed)throw new Error("Grain Analytics: Client has been destroyed");let n,r={};typeof e=="string"?(n={eventName:e,properties:t},r=i||{}):(n=e,r=t||{});let a=this.formatEvent(n);this.eventQueue.push(a),this.log(`Queued event: ${n.eventName}`,n.properties),(r.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=[],await this.sendEvents(e)}destroy(){this.isDestroyed=!0,this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=null),this.eventQueue.length>0&&(this.sendEventsWithBeacon([...this.eventQueue]).catch(()=>{}),this.eventQueue=[])}};function l(s){return new o(s)}var w=o;typeof window<"u"&&(window.Grain={GrainAnalytics:o,createGrainAnalytics:l});return m(E);})();
|
|
3
3
|
//# sourceMappingURL=index.global.js.map
|