@openpanel/react-native 1.3.0 → 1.4.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/README.md CHANGED
@@ -114,4 +114,36 @@ op.track('my_event', { foo: 'bar' });
114
114
  }
115
115
  ```
116
116
 
117
- For more information on how to use the SDK, check out the [Javascript SDK](https://openpanel.dev/docs/sdks/javascript#usage).
117
+ For more information on how to use the SDK, check out the [Javascript SDK](https://openpanel.dev/docs/sdks/javascript#usage).
118
+
119
+ ## Offline support
120
+
121
+ The SDK can buffer events when the device is offline and flush them once connectivity is restored. Events are stamped with a `__timestamp` at the time they are fired so they are recorded with the correct time even if they are delivered later.
122
+
123
+ Two optional peer dependencies enable this feature:
124
+
125
+ ```npm
126
+ npm install @react-native-async-storage/async-storage @react-native-community/netinfo
127
+ ```
128
+
129
+ Pass them to the constructor:
130
+
131
+ ```typescript
132
+ import { OpenPanel } from '@openpanel/react-native';
133
+ import AsyncStorage from '@react-native-async-storage/async-storage';
134
+ import NetInfo from '@react-native-community/netinfo';
135
+
136
+ const op = new OpenPanel({
137
+ clientId: '{YOUR_CLIENT_ID}',
138
+ clientSecret: '{YOUR_CLIENT_SECRET}',
139
+ // Persist the event queue across app restarts
140
+ storage: AsyncStorage,
141
+ // Automatically flush the queue when the device comes back online
142
+ networkInfo: NetInfo,
143
+ });
144
+ ```
145
+
146
+ Both options are independent — you can use either one or both:
147
+
148
+ - **`storage`** — persists the queue to disk so events survive app restarts while offline.
149
+ - **`networkInfo`** — flushes the queue automatically when connectivity is restored. Without this, the queue is flushed the next time the app becomes active.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var _=Object.create;var c=Object.defineProperty;var h=Object.getOwnPropertyDescriptor;var u=Object.getOwnPropertyNames;var A=Object.getPrototypeOf,v=Object.prototype.hasOwnProperty;var k=(t,r)=>{for(var e in r)c(t,e,{get:r[e],enumerable:!0})},o=(t,r,e,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(let p of u(r))!v.call(t,p)&&p!==e&&c(t,p,{get:()=>r[p],enumerable:!(s=h(r,p))||s.enumerable});return t},a=(t,r,e)=>(o(t,r,"default"),e&&o(e,r,"default")),f=(t,r,e)=>(e=t!=null?_(A(t)):{},o(r||!t||!t.__esModule?c(e,"default",{value:t,enumerable:!0}):e,t)),g=t=>o(c({},"__esModule",{value:!0}),t);var i={};k(i,{OpenPanel:()=>d});module.exports=g(i);var P=require("@openpanel/sdk"),n=f(require("expo-application"),1),m=f(require("expo-constants"),1),l=require("react-native");a(i,require("@openpanel/sdk"),module.exports);var d=class extends P.OpenPanel{constructor(e){super({...e,sdk:"react-native",sdkVersion:"1.3.0"});this.options=e;this.lastPath="";this.api.addHeader("User-Agent",m.default.getWebViewUserAgentAsync()),l.AppState.addEventListener("change",s=>{s==="active"&&this.setDefaultProperties()}),this.setDefaultProperties()}async setDefaultProperties(){this.setGlobalProperties({__version:n.nativeApplicationVersion,__buildNumber:n.nativeBuildVersion,__referrer:l.Platform.OS==="android"?await n.getInstallReferrerAsync():void 0})}track(e,s){return super.track(e,{...s,__path:this.lastPath})}screenView(e,s){this.lastPath=e,super.track("screen_view",{...s,__path:e})}};0&&(module.exports={OpenPanel,...require("@openpanel/sdk")});
1
+ "use strict";var g=Object.create;var u=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var v=Object.getOwnPropertyNames;var O=Object.getPrototypeOf,m=Object.prototype.hasOwnProperty;var w=(t,r)=>{for(var e in r)u(t,e,{get:r[e],enumerable:!0})},l=(t,r,e,i)=>{if(r&&typeof r=="object"||typeof r=="function")for(let a of v(r))!m.call(t,a)&&a!==e&&u(t,a,{get:()=>r[a],enumerable:!(i=P(r,a))||i.enumerable});return t},n=(t,r,e)=>(l(t,r,"default"),e&&l(e,r,"default")),h=(t,r,e)=>(e=t!=null?g(O(t)):{},l(r||!t||!t.__esModule?u(e,"default",{value:t,enumerable:!0}):e,t)),_=t=>l(u({},"__esModule",{value:!0}),t);var s={};w(s,{OpenPanel:()=>c});module.exports=_(s);var f=require("@openpanel/sdk"),o=h(require("expo-application"),1),k=h(require("expo-constants"),1),p=require("react-native");n(s,require("@openpanel/sdk"),module.exports);var d="@openpanel/offline_queue",c=class extends f.OpenPanel{constructor(e){super({...e,sdk:"react-native",sdkVersion:"1.4.0"});this.options=e;this.lastPath="";this.isOnline=!0;this.api.addHeader("User-Agent",k.default.getWebViewUserAgentAsync()),this.storage=e.storage,e.networkInfo&&(e.networkInfo.fetch().then(({isConnected:i})=>{this.isOnline=i??!0}),e.networkInfo.addEventListener(({isConnected:i})=>{let a=!this.isOnline;this.isOnline=i??!0,a&&this.isOnline&&this.flush()})),p.AppState.addEventListener("change",i=>{i==="active"&&(this.setDefaultProperties(),this.flush())}),this.setDefaultProperties(),this.loadPersistedQueue()}async setDefaultProperties(){this.setGlobalProperties({__version:o.nativeApplicationVersion,__buildNumber:o.nativeBuildVersion,__referrer:p.Platform.OS==="android"?await o.getInstallReferrerAsync():void 0})}async loadPersistedQueue(){if(this.storage)try{let e=await this.storage.getItem(d);if(e){let i=JSON.parse(e);Array.isArray(i)&&i.length>0&&(this.queue=[...i,...this.queue],this.flush())}}catch{this.log("Failed to load persisted queue")}}persistQueue(){this.storage&&this.storage.setItem(d,JSON.stringify(this.queue)).catch(()=>{this.log("Failed to persist queue")})}addQueue(e){super.addQueue(e),this.persistQueue()}async send(e){return this.options.filter&&!this.options.filter(e)?null:this.isOnline?await super.send(e):(this.addQueue(e),null)}flush(){this.isOnline&&(super.flush(),this.persistQueue())}track(e,i){return super.track(e,{...i,__path:this.lastPath})}screenView(e,i){this.lastPath=e,super.track("screen_view",{...i,__path:e})}};0&&(module.exports={OpenPanel,...require("@openpanel/sdk")});
package/dist/index.d.cts CHANGED
@@ -1,11 +1,52 @@
1
- import { OpenPanel as OpenPanel$1, OpenPanelOptions, TrackProperties } from '@openpanel/sdk';
1
+ import { OpenPanelOptions, OpenPanel as OpenPanel$1, TrackHandlerPayload, TrackProperties } from '@openpanel/sdk';
2
2
  export * from '@openpanel/sdk';
3
3
 
4
+ interface StorageLike {
5
+ getItem(key: string): Promise<string | null>;
6
+ setItem(key: string, value: string): Promise<void>;
7
+ }
8
+ interface NetworkStateLike {
9
+ isConnected: boolean | null;
10
+ }
11
+ interface NetworkInfoLike {
12
+ addEventListener(callback: (state: NetworkStateLike) => void): () => void;
13
+ fetch(): Promise<NetworkStateLike>;
14
+ }
15
+ interface ReactNativeOpenPanelOptions extends OpenPanelOptions {
16
+ /**
17
+ * Provide an AsyncStorage-compatible adapter to persist the event queue
18
+ * across app restarts (enables full offline support).
19
+ *
20
+ * @example
21
+ * import AsyncStorage from '@react-native-async-storage/async-storage';
22
+ * new OpenPanel({ clientId: '...', storage: AsyncStorage });
23
+ */
24
+ storage?: StorageLike;
25
+ /**
26
+ * Provide a NetInfo-compatible adapter to detect connectivity changes and
27
+ * automatically flush the queue when the device comes back online.
28
+ *
29
+ * @example
30
+ * import NetInfo from '@react-native-community/netinfo';
31
+ * new OpenPanel({ clientId: '...', networkInfo: NetInfo });
32
+ */
33
+ networkInfo?: NetworkInfoLike;
34
+ }
4
35
  declare class OpenPanel extends OpenPanel$1 {
5
- options: OpenPanelOptions;
36
+ options: ReactNativeOpenPanelOptions;
6
37
  private lastPath;
7
- constructor(options: OpenPanelOptions);
38
+ private readonly storage?;
39
+ private isOnline;
40
+ constructor(options: ReactNativeOpenPanelOptions);
8
41
  private setDefaultProperties;
42
+ private loadPersistedQueue;
43
+ private persistQueue;
44
+ addQueue(payload: TrackHandlerPayload): void;
45
+ send(payload: TrackHandlerPayload): Promise<void | {
46
+ deviceId: string;
47
+ sessionId: string;
48
+ } | null>;
49
+ flush(): void;
9
50
  track(name: string, properties?: TrackProperties): Promise<void | {
10
51
  deviceId: string;
11
52
  sessionId: string;
@@ -13,4 +54,4 @@ declare class OpenPanel extends OpenPanel$1 {
13
54
  screenView(route: string, properties?: TrackProperties): void;
14
55
  }
15
56
 
16
- export { OpenPanel };
57
+ export { OpenPanel, type ReactNativeOpenPanelOptions };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,52 @@
1
- import { OpenPanel as OpenPanel$1, OpenPanelOptions, TrackProperties } from '@openpanel/sdk';
1
+ import { OpenPanelOptions, OpenPanel as OpenPanel$1, TrackHandlerPayload, TrackProperties } from '@openpanel/sdk';
2
2
  export * from '@openpanel/sdk';
3
3
 
4
+ interface StorageLike {
5
+ getItem(key: string): Promise<string | null>;
6
+ setItem(key: string, value: string): Promise<void>;
7
+ }
8
+ interface NetworkStateLike {
9
+ isConnected: boolean | null;
10
+ }
11
+ interface NetworkInfoLike {
12
+ addEventListener(callback: (state: NetworkStateLike) => void): () => void;
13
+ fetch(): Promise<NetworkStateLike>;
14
+ }
15
+ interface ReactNativeOpenPanelOptions extends OpenPanelOptions {
16
+ /**
17
+ * Provide an AsyncStorage-compatible adapter to persist the event queue
18
+ * across app restarts (enables full offline support).
19
+ *
20
+ * @example
21
+ * import AsyncStorage from '@react-native-async-storage/async-storage';
22
+ * new OpenPanel({ clientId: '...', storage: AsyncStorage });
23
+ */
24
+ storage?: StorageLike;
25
+ /**
26
+ * Provide a NetInfo-compatible adapter to detect connectivity changes and
27
+ * automatically flush the queue when the device comes back online.
28
+ *
29
+ * @example
30
+ * import NetInfo from '@react-native-community/netinfo';
31
+ * new OpenPanel({ clientId: '...', networkInfo: NetInfo });
32
+ */
33
+ networkInfo?: NetworkInfoLike;
34
+ }
4
35
  declare class OpenPanel extends OpenPanel$1 {
5
- options: OpenPanelOptions;
36
+ options: ReactNativeOpenPanelOptions;
6
37
  private lastPath;
7
- constructor(options: OpenPanelOptions);
38
+ private readonly storage?;
39
+ private isOnline;
40
+ constructor(options: ReactNativeOpenPanelOptions);
8
41
  private setDefaultProperties;
42
+ private loadPersistedQueue;
43
+ private persistQueue;
44
+ addQueue(payload: TrackHandlerPayload): void;
45
+ send(payload: TrackHandlerPayload): Promise<void | {
46
+ deviceId: string;
47
+ sessionId: string;
48
+ } | null>;
49
+ flush(): void;
9
50
  track(name: string, properties?: TrackProperties): Promise<void | {
10
51
  deviceId: string;
11
52
  sessionId: string;
@@ -13,4 +54,4 @@ declare class OpenPanel extends OpenPanel$1 {
13
54
  screenView(route: string, properties?: TrackProperties): void;
14
55
  }
15
56
 
16
- export { OpenPanel };
57
+ export { OpenPanel, type ReactNativeOpenPanelOptions };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{OpenPanel as s}from"@openpanel/sdk";import*as t from"expo-application";import a from"expo-constants";import{AppState as n,Platform as p}from"react-native";export*from"@openpanel/sdk";var i=class extends s{constructor(e){super({...e,sdk:"react-native",sdkVersion:"1.3.0"});this.options=e;this.lastPath="";this.api.addHeader("User-Agent",a.getWebViewUserAgentAsync()),n.addEventListener("change",r=>{r==="active"&&this.setDefaultProperties()}),this.setDefaultProperties()}async setDefaultProperties(){this.setGlobalProperties({__version:t.nativeApplicationVersion,__buildNumber:t.nativeBuildVersion,__referrer:p.OS==="android"?await t.getInstallReferrerAsync():void 0})}track(e,r){return super.track(e,{...r,__path:this.lastPath})}screenView(e,r){this.lastPath=e,super.track("screen_view",{...r,__path:e})}};export{i as OpenPanel};
1
+ import{OpenPanel as a}from"@openpanel/sdk";import*as i from"expo-application";import o from"expo-constants";import{AppState as l,Platform as u}from"react-native";export*from"@openpanel/sdk";var r="@openpanel/offline_queue",s=class extends a{constructor(e){super({...e,sdk:"react-native",sdkVersion:"1.4.0"});this.options=e;this.lastPath="";this.isOnline=!0;this.api.addHeader("User-Agent",o.getWebViewUserAgentAsync()),this.storage=e.storage,e.networkInfo&&(e.networkInfo.fetch().then(({isConnected:t})=>{this.isOnline=t??!0}),e.networkInfo.addEventListener(({isConnected:t})=>{let n=!this.isOnline;this.isOnline=t??!0,n&&this.isOnline&&this.flush()})),l.addEventListener("change",t=>{t==="active"&&(this.setDefaultProperties(),this.flush())}),this.setDefaultProperties(),this.loadPersistedQueue()}async setDefaultProperties(){this.setGlobalProperties({__version:i.nativeApplicationVersion,__buildNumber:i.nativeBuildVersion,__referrer:u.OS==="android"?await i.getInstallReferrerAsync():void 0})}async loadPersistedQueue(){if(this.storage)try{let e=await this.storage.getItem(r);if(e){let t=JSON.parse(e);Array.isArray(t)&&t.length>0&&(this.queue=[...t,...this.queue],this.flush())}}catch{this.log("Failed to load persisted queue")}}persistQueue(){this.storage&&this.storage.setItem(r,JSON.stringify(this.queue)).catch(()=>{this.log("Failed to persist queue")})}addQueue(e){super.addQueue(e),this.persistQueue()}async send(e){return this.options.filter&&!this.options.filter(e)?null:this.isOnline?await super.send(e):(this.addQueue(e),null)}flush(){this.isOnline&&(super.flush(),this.persistQueue())}track(e,t){return super.track(e,{...t,__path:this.lastPath})}screenView(e,t){this.lastPath=e,super.track("screen_view",{...t,__path:e})}};export{s as OpenPanel};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpanel/react-native",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "module": "./dist/index.js",
5
5
  "config": {
6
6
  "docPath": "apps/public/content/docs/(tracking)/sdks/react-native.mdx"