@sendoracloud/sdk-react-native 0.10.4 → 0.11.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 +12 -0
- package/dist/index.cjs +105 -1
- package/dist/index.d.cts +27 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +112 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,6 +86,18 @@ SendoraCloud.reset();
|
|
|
86
86
|
environment?: "prod" | "staging" | "dev"; // Routing hint.
|
|
87
87
|
projectId?: string; // Optional project scoping.
|
|
88
88
|
consentedByDefault?: boolean; // Defaults to true.
|
|
89
|
+
/**
|
|
90
|
+
* Auto-collect lifecycle events. Default true. Object form:
|
|
91
|
+
* { appOpen?, sessionStart?, appBackground? } for granular control.
|
|
92
|
+
* - appOpen — `app.opened` once per init.
|
|
93
|
+
* - sessionStart — `session.start` once per session (idle >sessionIdleMs).
|
|
94
|
+
* - appBackground — AppState 'change' fires `app.foregrounded` /
|
|
95
|
+
* `app.backgrounded`.
|
|
96
|
+
* Note: `screen.viewed` is NOT auto — call `SendoraCloud.screen(name)`
|
|
97
|
+
* from your navigation listener (RN has no canonical router).
|
|
98
|
+
*/
|
|
99
|
+
autoTrack?: boolean | { appOpen?: boolean; sessionStart?: boolean; appBackground?: boolean };
|
|
100
|
+
sessionIdleMs?: number; // Idle threshold for session expiry. Default 30 min.
|
|
89
101
|
}
|
|
90
102
|
```
|
|
91
103
|
|
package/dist/index.cjs
CHANGED
|
@@ -799,8 +799,88 @@ function decodeJwtPayload(token) {
|
|
|
799
799
|
}
|
|
800
800
|
}
|
|
801
801
|
|
|
802
|
+
// src/auto-track.ts
|
|
803
|
+
var DEFAULT_IDLE_MS = 30 * 60 * 1e3;
|
|
804
|
+
function resolveFlags(cfg) {
|
|
805
|
+
if (cfg === false) {
|
|
806
|
+
return { appOpen: false, sessionStart: false, appBackground: false };
|
|
807
|
+
}
|
|
808
|
+
if (cfg === true || cfg === void 0) {
|
|
809
|
+
return { appOpen: true, sessionStart: true, appBackground: true };
|
|
810
|
+
}
|
|
811
|
+
return {
|
|
812
|
+
appOpen: cfg.appOpen ?? true,
|
|
813
|
+
sessionStart: cfg.sessionStart ?? true,
|
|
814
|
+
appBackground: cfg.appBackground ?? true
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function installAutoTrack(args) {
|
|
818
|
+
const flags = resolveFlags(args.config.autoTrack);
|
|
819
|
+
const idleMs = args.config.sessionIdleMs ?? DEFAULT_IDLE_MS;
|
|
820
|
+
let lastActivity = Date.now();
|
|
821
|
+
const cleanups = [];
|
|
822
|
+
function newSessionId() {
|
|
823
|
+
return `sess_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
824
|
+
}
|
|
825
|
+
function ensureSession(emitStart) {
|
|
826
|
+
const now = Date.now();
|
|
827
|
+
let id = args.getSessionId();
|
|
828
|
+
if (!id || now - lastActivity > idleMs) {
|
|
829
|
+
id = newSessionId();
|
|
830
|
+
args.setSessionId(id);
|
|
831
|
+
if (emitStart && flags.sessionStart) {
|
|
832
|
+
args.fire("session.start", { sessionId: id });
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
lastActivity = now;
|
|
836
|
+
return id;
|
|
837
|
+
}
|
|
838
|
+
const sessionId = ensureSession(
|
|
839
|
+
/* emitStart */
|
|
840
|
+
flags.sessionStart
|
|
841
|
+
);
|
|
842
|
+
if (flags.appOpen) {
|
|
843
|
+
args.fire("app.opened", { sessionId });
|
|
844
|
+
}
|
|
845
|
+
if (flags.appBackground) {
|
|
846
|
+
let AppState = null;
|
|
847
|
+
try {
|
|
848
|
+
AppState = require("react-native").AppState;
|
|
849
|
+
} catch {
|
|
850
|
+
AppState = null;
|
|
851
|
+
}
|
|
852
|
+
if (AppState && typeof AppState.addEventListener === "function") {
|
|
853
|
+
const handler = (state) => {
|
|
854
|
+
ensureSession(true);
|
|
855
|
+
if (state === "background" || state === "inactive") {
|
|
856
|
+
args.fire("app.backgrounded", { sessionId: args.getSessionId() });
|
|
857
|
+
} else if (state === "active") {
|
|
858
|
+
args.fire("app.foregrounded", { sessionId: args.getSessionId() });
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
const sub = AppState.addEventListener("change", handler);
|
|
862
|
+
cleanups.push(() => {
|
|
863
|
+
if (typeof sub === "function") {
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
if (sub && typeof sub.remove === "function") {
|
|
867
|
+
sub.remove();
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
return () => {
|
|
873
|
+
for (const fn of cleanups) {
|
|
874
|
+
try {
|
|
875
|
+
fn();
|
|
876
|
+
} catch {
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
|
|
802
882
|
// src/index.ts
|
|
803
|
-
var SDK_VERSION = "0.
|
|
883
|
+
var SDK_VERSION = "0.11.0";
|
|
804
884
|
var DEFAULT_API_URL = "https://api.sendoracloud.com";
|
|
805
885
|
var ANON_KEY = "anon_id";
|
|
806
886
|
var USER_ID_KEY = "user_id";
|
|
@@ -864,6 +944,8 @@ var SendoraSDK = class {
|
|
|
864
944
|
this.userId = null;
|
|
865
945
|
this.consentGranted = true;
|
|
866
946
|
this.debug = false;
|
|
947
|
+
this.sessionId = "";
|
|
948
|
+
this.autoTrackCleanup = null;
|
|
867
949
|
}
|
|
868
950
|
/** Initialize the SDK. Must be awaited at app startup before identify/track. */
|
|
869
951
|
async init(config) {
|
|
@@ -938,6 +1020,20 @@ var SendoraSDK = class {
|
|
|
938
1020
|
});
|
|
939
1021
|
this.auth.hydrate();
|
|
940
1022
|
this.ready = true;
|
|
1023
|
+
if (config.autoTrack !== false) {
|
|
1024
|
+
this.autoTrackCleanup = installAutoTrack({
|
|
1025
|
+
config: this.config,
|
|
1026
|
+
fire: (eventType, properties) => this.track(eventType, properties),
|
|
1027
|
+
getSessionId: () => this.sessionId,
|
|
1028
|
+
setSessionId: (id) => {
|
|
1029
|
+
this.sessionId = id;
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
/** Current logical session id, if auto-track is enabled. */
|
|
1035
|
+
getSessionId() {
|
|
1036
|
+
return this.sessionId;
|
|
941
1037
|
}
|
|
942
1038
|
/** Stable anonymous identifier for this install — survives app restarts via AsyncStorage. */
|
|
943
1039
|
getAnonymousId() {
|
|
@@ -1045,9 +1141,17 @@ var SendoraSDK = class {
|
|
|
1045
1141
|
async reset() {
|
|
1046
1142
|
this.userId = null;
|
|
1047
1143
|
this.anonId = uuid();
|
|
1144
|
+
this.sessionId = "";
|
|
1048
1145
|
await this.storage.clearAll();
|
|
1049
1146
|
this.storage.set(ANON_KEY, this.anonId);
|
|
1050
1147
|
}
|
|
1148
|
+
/** Tear down auto-track listeners. Call in test harnesses. */
|
|
1149
|
+
destroy() {
|
|
1150
|
+
if (this.autoTrackCleanup) {
|
|
1151
|
+
this.autoTrackCleanup();
|
|
1152
|
+
this.autoTrackCleanup = null;
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1051
1155
|
async sendEvent(kind, payload) {
|
|
1052
1156
|
if (!this.config) return;
|
|
1053
1157
|
if (!this.consentGranted) return;
|
package/dist/index.d.cts
CHANGED
|
@@ -309,6 +309,27 @@ interface SendoraConfig {
|
|
|
309
309
|
consentedByDefault?: boolean;
|
|
310
310
|
/** Verbose error logging (auth-error codes + Zod field errors). Default false. */
|
|
311
311
|
debug?: boolean;
|
|
312
|
+
/**
|
|
313
|
+
* Auto-collect lifecycle events. Mirrors Firebase Analytics' auto-collected
|
|
314
|
+
* surface. `true` = all enabled (default). Pass an object for granular
|
|
315
|
+
* control or `false` to disable.
|
|
316
|
+
*
|
|
317
|
+
* - `appOpen` — fire `app.opened` on init. Default true.
|
|
318
|
+
* - `sessionStart` — emit `session.start` once per logical session
|
|
319
|
+
* (idle >`sessionIdleMs` closes session). Default true.
|
|
320
|
+
* - `appBackground` — emit `app.backgrounded`/`app.foregrounded` on
|
|
321
|
+
* AppState transitions. Default true.
|
|
322
|
+
*
|
|
323
|
+
* Note: screen view tracking is NOT auto — RN has no canonical router.
|
|
324
|
+
* Call `SendoraCloud.screen(name)` from your navigation listener.
|
|
325
|
+
*/
|
|
326
|
+
autoTrack?: boolean | {
|
|
327
|
+
appOpen?: boolean;
|
|
328
|
+
sessionStart?: boolean;
|
|
329
|
+
appBackground?: boolean;
|
|
330
|
+
};
|
|
331
|
+
/** Idle threshold (ms) for session expiry; default 30 minutes. */
|
|
332
|
+
sessionIdleMs?: number;
|
|
312
333
|
}
|
|
313
334
|
type TraitValue = string | number | boolean | null | undefined;
|
|
314
335
|
interface IdentifyTraits {
|
|
@@ -377,10 +398,14 @@ declare class SendoraSDK {
|
|
|
377
398
|
private userId;
|
|
378
399
|
private consentGranted;
|
|
379
400
|
private debug;
|
|
401
|
+
private sessionId;
|
|
402
|
+
private autoTrackCleanup;
|
|
380
403
|
/** Lazy-initialised in init() once we know the apiUrl + publicKey. */
|
|
381
404
|
auth: Auth;
|
|
382
405
|
/** Initialize the SDK. Must be awaited at app startup before identify/track. */
|
|
383
406
|
init(config: SendoraConfig): Promise<void>;
|
|
407
|
+
/** Current logical session id, if auto-track is enabled. */
|
|
408
|
+
getSessionId(): string;
|
|
384
409
|
/** Stable anonymous identifier for this install — survives app restarts via AsyncStorage. */
|
|
385
410
|
getAnonymousId(): string;
|
|
386
411
|
/** Current identified user id, if any. */
|
|
@@ -406,6 +431,8 @@ declare class SendoraSDK {
|
|
|
406
431
|
* after doesn't leave stale identity on disk.
|
|
407
432
|
*/
|
|
408
433
|
reset(): Promise<void>;
|
|
434
|
+
/** Tear down auto-track listeners. Call in test harnesses. */
|
|
435
|
+
destroy(): void;
|
|
409
436
|
private sendEvent;
|
|
410
437
|
private assertReady;
|
|
411
438
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -309,6 +309,27 @@ interface SendoraConfig {
|
|
|
309
309
|
consentedByDefault?: boolean;
|
|
310
310
|
/** Verbose error logging (auth-error codes + Zod field errors). Default false. */
|
|
311
311
|
debug?: boolean;
|
|
312
|
+
/**
|
|
313
|
+
* Auto-collect lifecycle events. Mirrors Firebase Analytics' auto-collected
|
|
314
|
+
* surface. `true` = all enabled (default). Pass an object for granular
|
|
315
|
+
* control or `false` to disable.
|
|
316
|
+
*
|
|
317
|
+
* - `appOpen` — fire `app.opened` on init. Default true.
|
|
318
|
+
* - `sessionStart` — emit `session.start` once per logical session
|
|
319
|
+
* (idle >`sessionIdleMs` closes session). Default true.
|
|
320
|
+
* - `appBackground` — emit `app.backgrounded`/`app.foregrounded` on
|
|
321
|
+
* AppState transitions. Default true.
|
|
322
|
+
*
|
|
323
|
+
* Note: screen view tracking is NOT auto — RN has no canonical router.
|
|
324
|
+
* Call `SendoraCloud.screen(name)` from your navigation listener.
|
|
325
|
+
*/
|
|
326
|
+
autoTrack?: boolean | {
|
|
327
|
+
appOpen?: boolean;
|
|
328
|
+
sessionStart?: boolean;
|
|
329
|
+
appBackground?: boolean;
|
|
330
|
+
};
|
|
331
|
+
/** Idle threshold (ms) for session expiry; default 30 minutes. */
|
|
332
|
+
sessionIdleMs?: number;
|
|
312
333
|
}
|
|
313
334
|
type TraitValue = string | number | boolean | null | undefined;
|
|
314
335
|
interface IdentifyTraits {
|
|
@@ -377,10 +398,14 @@ declare class SendoraSDK {
|
|
|
377
398
|
private userId;
|
|
378
399
|
private consentGranted;
|
|
379
400
|
private debug;
|
|
401
|
+
private sessionId;
|
|
402
|
+
private autoTrackCleanup;
|
|
380
403
|
/** Lazy-initialised in init() once we know the apiUrl + publicKey. */
|
|
381
404
|
auth: Auth;
|
|
382
405
|
/** Initialize the SDK. Must be awaited at app startup before identify/track. */
|
|
383
406
|
init(config: SendoraConfig): Promise<void>;
|
|
407
|
+
/** Current logical session id, if auto-track is enabled. */
|
|
408
|
+
getSessionId(): string;
|
|
384
409
|
/** Stable anonymous identifier for this install — survives app restarts via AsyncStorage. */
|
|
385
410
|
getAnonymousId(): string;
|
|
386
411
|
/** Current identified user id, if any. */
|
|
@@ -406,6 +431,8 @@ declare class SendoraSDK {
|
|
|
406
431
|
* after doesn't leave stale identity on disk.
|
|
407
432
|
*/
|
|
408
433
|
reset(): Promise<void>;
|
|
434
|
+
/** Tear down auto-track listeners. Call in test harnesses. */
|
|
435
|
+
destroy(): void;
|
|
409
436
|
private sendEvent;
|
|
410
437
|
private assertReady;
|
|
411
438
|
}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
1
8
|
// src/storage.ts
|
|
2
9
|
var PREFIX = "sendora_";
|
|
3
10
|
var asyncStoragePromise = null;
|
|
@@ -759,8 +766,88 @@ function decodeJwtPayload(token) {
|
|
|
759
766
|
}
|
|
760
767
|
}
|
|
761
768
|
|
|
769
|
+
// src/auto-track.ts
|
|
770
|
+
var DEFAULT_IDLE_MS = 30 * 60 * 1e3;
|
|
771
|
+
function resolveFlags(cfg) {
|
|
772
|
+
if (cfg === false) {
|
|
773
|
+
return { appOpen: false, sessionStart: false, appBackground: false };
|
|
774
|
+
}
|
|
775
|
+
if (cfg === true || cfg === void 0) {
|
|
776
|
+
return { appOpen: true, sessionStart: true, appBackground: true };
|
|
777
|
+
}
|
|
778
|
+
return {
|
|
779
|
+
appOpen: cfg.appOpen ?? true,
|
|
780
|
+
sessionStart: cfg.sessionStart ?? true,
|
|
781
|
+
appBackground: cfg.appBackground ?? true
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
function installAutoTrack(args) {
|
|
785
|
+
const flags = resolveFlags(args.config.autoTrack);
|
|
786
|
+
const idleMs = args.config.sessionIdleMs ?? DEFAULT_IDLE_MS;
|
|
787
|
+
let lastActivity = Date.now();
|
|
788
|
+
const cleanups = [];
|
|
789
|
+
function newSessionId() {
|
|
790
|
+
return `sess_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
791
|
+
}
|
|
792
|
+
function ensureSession(emitStart) {
|
|
793
|
+
const now = Date.now();
|
|
794
|
+
let id = args.getSessionId();
|
|
795
|
+
if (!id || now - lastActivity > idleMs) {
|
|
796
|
+
id = newSessionId();
|
|
797
|
+
args.setSessionId(id);
|
|
798
|
+
if (emitStart && flags.sessionStart) {
|
|
799
|
+
args.fire("session.start", { sessionId: id });
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
lastActivity = now;
|
|
803
|
+
return id;
|
|
804
|
+
}
|
|
805
|
+
const sessionId = ensureSession(
|
|
806
|
+
/* emitStart */
|
|
807
|
+
flags.sessionStart
|
|
808
|
+
);
|
|
809
|
+
if (flags.appOpen) {
|
|
810
|
+
args.fire("app.opened", { sessionId });
|
|
811
|
+
}
|
|
812
|
+
if (flags.appBackground) {
|
|
813
|
+
let AppState = null;
|
|
814
|
+
try {
|
|
815
|
+
AppState = __require("react-native").AppState;
|
|
816
|
+
} catch {
|
|
817
|
+
AppState = null;
|
|
818
|
+
}
|
|
819
|
+
if (AppState && typeof AppState.addEventListener === "function") {
|
|
820
|
+
const handler = (state) => {
|
|
821
|
+
ensureSession(true);
|
|
822
|
+
if (state === "background" || state === "inactive") {
|
|
823
|
+
args.fire("app.backgrounded", { sessionId: args.getSessionId() });
|
|
824
|
+
} else if (state === "active") {
|
|
825
|
+
args.fire("app.foregrounded", { sessionId: args.getSessionId() });
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
const sub = AppState.addEventListener("change", handler);
|
|
829
|
+
cleanups.push(() => {
|
|
830
|
+
if (typeof sub === "function") {
|
|
831
|
+
return;
|
|
832
|
+
}
|
|
833
|
+
if (sub && typeof sub.remove === "function") {
|
|
834
|
+
sub.remove();
|
|
835
|
+
}
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
return () => {
|
|
840
|
+
for (const fn of cleanups) {
|
|
841
|
+
try {
|
|
842
|
+
fn();
|
|
843
|
+
} catch {
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
|
|
762
849
|
// src/index.ts
|
|
763
|
-
var SDK_VERSION = "0.
|
|
850
|
+
var SDK_VERSION = "0.11.0";
|
|
764
851
|
var DEFAULT_API_URL = "https://api.sendoracloud.com";
|
|
765
852
|
var ANON_KEY = "anon_id";
|
|
766
853
|
var USER_ID_KEY = "user_id";
|
|
@@ -824,6 +911,8 @@ var SendoraSDK = class {
|
|
|
824
911
|
this.userId = null;
|
|
825
912
|
this.consentGranted = true;
|
|
826
913
|
this.debug = false;
|
|
914
|
+
this.sessionId = "";
|
|
915
|
+
this.autoTrackCleanup = null;
|
|
827
916
|
}
|
|
828
917
|
/** Initialize the SDK. Must be awaited at app startup before identify/track. */
|
|
829
918
|
async init(config) {
|
|
@@ -898,6 +987,20 @@ var SendoraSDK = class {
|
|
|
898
987
|
});
|
|
899
988
|
this.auth.hydrate();
|
|
900
989
|
this.ready = true;
|
|
990
|
+
if (config.autoTrack !== false) {
|
|
991
|
+
this.autoTrackCleanup = installAutoTrack({
|
|
992
|
+
config: this.config,
|
|
993
|
+
fire: (eventType, properties) => this.track(eventType, properties),
|
|
994
|
+
getSessionId: () => this.sessionId,
|
|
995
|
+
setSessionId: (id) => {
|
|
996
|
+
this.sessionId = id;
|
|
997
|
+
}
|
|
998
|
+
});
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
/** Current logical session id, if auto-track is enabled. */
|
|
1002
|
+
getSessionId() {
|
|
1003
|
+
return this.sessionId;
|
|
901
1004
|
}
|
|
902
1005
|
/** Stable anonymous identifier for this install — survives app restarts via AsyncStorage. */
|
|
903
1006
|
getAnonymousId() {
|
|
@@ -1005,9 +1108,17 @@ var SendoraSDK = class {
|
|
|
1005
1108
|
async reset() {
|
|
1006
1109
|
this.userId = null;
|
|
1007
1110
|
this.anonId = uuid();
|
|
1111
|
+
this.sessionId = "";
|
|
1008
1112
|
await this.storage.clearAll();
|
|
1009
1113
|
this.storage.set(ANON_KEY, this.anonId);
|
|
1010
1114
|
}
|
|
1115
|
+
/** Tear down auto-track listeners. Call in test harnesses. */
|
|
1116
|
+
destroy() {
|
|
1117
|
+
if (this.autoTrackCleanup) {
|
|
1118
|
+
this.autoTrackCleanup();
|
|
1119
|
+
this.autoTrackCleanup = null;
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1011
1122
|
async sendEvent(kind, payload) {
|
|
1012
1123
|
if (!this.config) return;
|
|
1013
1124
|
if (!this.consentGranted) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendoracloud/sdk-react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Sendora Cloud React Native + Expo SDK — analytics, identity, push-token registration, auth. Expo Go compatible, no native modules beyond AsyncStorage.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|