@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 CHANGED
@@ -1,4 +1,4 @@
1
- # @grain-analytics/web
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 @grain-analytics/web
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 '@grain-analytics/web';
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/@grain-analytics/web/dist/index.global.js"></script>
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'
@@ -1,4 +1,4 @@
1
- /* Grain Analytics Web SDK v1.0.0 | MIT License | Development Build */
1
+ /* Grain Analytics Web SDK v1.0.1 | MIT License | Development Build */
2
2
  "use strict";
3
3
  var Grain = (() => {
4
4
  var __defProp = Object.defineProperty;
@@ -1,3 +1,3 @@
1
- /* Grain Analytics Web SDK v1.0.0 | MIT License */
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@grainql/analytics-web",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
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",