@sendoracloud/sdk-react-native 0.10.4 → 0.12.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 +129 -1
- package/dist/index.d.cts +37 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +129 -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,90 @@ 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
|
+
const dyn = globalThis.require;
|
|
849
|
+
const mod = dyn ? dyn("react-native") : null;
|
|
850
|
+
AppState = mod?.AppState ?? null;
|
|
851
|
+
} catch {
|
|
852
|
+
AppState = null;
|
|
853
|
+
}
|
|
854
|
+
if (AppState && typeof AppState.addEventListener === "function") {
|
|
855
|
+
const handler = (state) => {
|
|
856
|
+
ensureSession(true);
|
|
857
|
+
if (state === "background" || state === "inactive") {
|
|
858
|
+
args.fire("app.backgrounded", { sessionId: args.getSessionId() });
|
|
859
|
+
} else if (state === "active") {
|
|
860
|
+
args.fire("app.foregrounded", { sessionId: args.getSessionId() });
|
|
861
|
+
}
|
|
862
|
+
};
|
|
863
|
+
const sub = AppState.addEventListener("change", handler);
|
|
864
|
+
cleanups.push(() => {
|
|
865
|
+
if (typeof sub === "function") {
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
if (sub && typeof sub.remove === "function") {
|
|
869
|
+
sub.remove();
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return () => {
|
|
875
|
+
for (const fn of cleanups) {
|
|
876
|
+
try {
|
|
877
|
+
fn();
|
|
878
|
+
} catch {
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
|
|
802
884
|
// src/index.ts
|
|
803
|
-
var SDK_VERSION = "0.
|
|
885
|
+
var SDK_VERSION = "0.11.0";
|
|
804
886
|
var DEFAULT_API_URL = "https://api.sendoracloud.com";
|
|
805
887
|
var ANON_KEY = "anon_id";
|
|
806
888
|
var USER_ID_KEY = "user_id";
|
|
@@ -864,6 +946,8 @@ var SendoraSDK = class {
|
|
|
864
946
|
this.userId = null;
|
|
865
947
|
this.consentGranted = true;
|
|
866
948
|
this.debug = false;
|
|
949
|
+
this.sessionId = "";
|
|
950
|
+
this.autoTrackCleanup = null;
|
|
867
951
|
}
|
|
868
952
|
/** Initialize the SDK. Must be awaited at app startup before identify/track. */
|
|
869
953
|
async init(config) {
|
|
@@ -938,6 +1022,20 @@ var SendoraSDK = class {
|
|
|
938
1022
|
});
|
|
939
1023
|
this.auth.hydrate();
|
|
940
1024
|
this.ready = true;
|
|
1025
|
+
if (config.autoTrack !== false) {
|
|
1026
|
+
this.autoTrackCleanup = installAutoTrack({
|
|
1027
|
+
config: this.config,
|
|
1028
|
+
fire: (eventType, properties) => this.track(eventType, properties),
|
|
1029
|
+
getSessionId: () => this.sessionId,
|
|
1030
|
+
setSessionId: (id) => {
|
|
1031
|
+
this.sessionId = id;
|
|
1032
|
+
}
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
/** Current logical session id, if auto-track is enabled. */
|
|
1037
|
+
getSessionId() {
|
|
1038
|
+
return this.sessionId;
|
|
941
1039
|
}
|
|
942
1040
|
/** Stable anonymous identifier for this install — survives app restarts via AsyncStorage. */
|
|
943
1041
|
getAnonymousId() {
|
|
@@ -1037,6 +1135,28 @@ var SendoraSDK = class {
|
|
|
1037
1135
|
}
|
|
1038
1136
|
return { tokenId: data.tokenId, created: data.created === true };
|
|
1039
1137
|
}
|
|
1138
|
+
/**
|
|
1139
|
+
* Push namespace — mirrors the iOS / Android / Web SDK shape so
|
|
1140
|
+
* `sendora.push.registerToken(...)` works consistently across all 5 SDKs.
|
|
1141
|
+
* Backed by the same registerPushToken() implementation; kept top-level
|
|
1142
|
+
* too for backwards compatibility with the s47 release.
|
|
1143
|
+
*/
|
|
1144
|
+
get push() {
|
|
1145
|
+
const self = this;
|
|
1146
|
+
return {
|
|
1147
|
+
registerToken: (reg) => self.registerPushToken(reg),
|
|
1148
|
+
trackOpen: async (sendId, clickAction) => {
|
|
1149
|
+
if (!self.config) throw new Error("[sendoracloud] init() must complete before push.trackOpen().");
|
|
1150
|
+
const body = { sendId };
|
|
1151
|
+
if (clickAction !== void 0) body.clickAction = clickAction;
|
|
1152
|
+
await post(
|
|
1153
|
+
{ apiUrl: self.config.apiUrl, publicKey: self.config.publicKey, debug: self.debug },
|
|
1154
|
+
"/push/track-open",
|
|
1155
|
+
body
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1040
1160
|
/**
|
|
1041
1161
|
* Clear identity + rotate the anonymous id. Call on logout. Awaits
|
|
1042
1162
|
* the AsyncStorage writes so a caller who kills the app immediately
|
|
@@ -1045,9 +1165,17 @@ var SendoraSDK = class {
|
|
|
1045
1165
|
async reset() {
|
|
1046
1166
|
this.userId = null;
|
|
1047
1167
|
this.anonId = uuid();
|
|
1168
|
+
this.sessionId = "";
|
|
1048
1169
|
await this.storage.clearAll();
|
|
1049
1170
|
this.storage.set(ANON_KEY, this.anonId);
|
|
1050
1171
|
}
|
|
1172
|
+
/** Tear down auto-track listeners. Call in test harnesses. */
|
|
1173
|
+
destroy() {
|
|
1174
|
+
if (this.autoTrackCleanup) {
|
|
1175
|
+
this.autoTrackCleanup();
|
|
1176
|
+
this.autoTrackCleanup = null;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1051
1179
|
async sendEvent(kind, payload) {
|
|
1052
1180
|
if (!this.config) return;
|
|
1053
1181
|
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. */
|
|
@@ -400,12 +425,24 @@ declare class SendoraSDK {
|
|
|
400
425
|
* `@react-native-firebase/messaging`.
|
|
401
426
|
*/
|
|
402
427
|
registerPushToken(reg: PushTokenRegistration): Promise<PushTokenReceipt>;
|
|
428
|
+
/**
|
|
429
|
+
* Push namespace — mirrors the iOS / Android / Web SDK shape so
|
|
430
|
+
* `sendora.push.registerToken(...)` works consistently across all 5 SDKs.
|
|
431
|
+
* Backed by the same registerPushToken() implementation; kept top-level
|
|
432
|
+
* too for backwards compatibility with the s47 release.
|
|
433
|
+
*/
|
|
434
|
+
get push(): {
|
|
435
|
+
registerToken: (reg: PushTokenRegistration) => Promise<PushTokenReceipt>;
|
|
436
|
+
trackOpen: (sendId: string, clickAction?: string) => Promise<void>;
|
|
437
|
+
};
|
|
403
438
|
/**
|
|
404
439
|
* Clear identity + rotate the anonymous id. Call on logout. Awaits
|
|
405
440
|
* the AsyncStorage writes so a caller who kills the app immediately
|
|
406
441
|
* after doesn't leave stale identity on disk.
|
|
407
442
|
*/
|
|
408
443
|
reset(): Promise<void>;
|
|
444
|
+
/** Tear down auto-track listeners. Call in test harnesses. */
|
|
445
|
+
destroy(): void;
|
|
409
446
|
private sendEvent;
|
|
410
447
|
private assertReady;
|
|
411
448
|
}
|
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. */
|
|
@@ -400,12 +425,24 @@ declare class SendoraSDK {
|
|
|
400
425
|
* `@react-native-firebase/messaging`.
|
|
401
426
|
*/
|
|
402
427
|
registerPushToken(reg: PushTokenRegistration): Promise<PushTokenReceipt>;
|
|
428
|
+
/**
|
|
429
|
+
* Push namespace — mirrors the iOS / Android / Web SDK shape so
|
|
430
|
+
* `sendora.push.registerToken(...)` works consistently across all 5 SDKs.
|
|
431
|
+
* Backed by the same registerPushToken() implementation; kept top-level
|
|
432
|
+
* too for backwards compatibility with the s47 release.
|
|
433
|
+
*/
|
|
434
|
+
get push(): {
|
|
435
|
+
registerToken: (reg: PushTokenRegistration) => Promise<PushTokenReceipt>;
|
|
436
|
+
trackOpen: (sendId: string, clickAction?: string) => Promise<void>;
|
|
437
|
+
};
|
|
403
438
|
/**
|
|
404
439
|
* Clear identity + rotate the anonymous id. Call on logout. Awaits
|
|
405
440
|
* the AsyncStorage writes so a caller who kills the app immediately
|
|
406
441
|
* after doesn't leave stale identity on disk.
|
|
407
442
|
*/
|
|
408
443
|
reset(): Promise<void>;
|
|
444
|
+
/** Tear down auto-track listeners. Call in test harnesses. */
|
|
445
|
+
destroy(): void;
|
|
409
446
|
private sendEvent;
|
|
410
447
|
private assertReady;
|
|
411
448
|
}
|
package/dist/index.js
CHANGED
|
@@ -759,8 +759,90 @@ function decodeJwtPayload(token) {
|
|
|
759
759
|
}
|
|
760
760
|
}
|
|
761
761
|
|
|
762
|
+
// src/auto-track.ts
|
|
763
|
+
var DEFAULT_IDLE_MS = 30 * 60 * 1e3;
|
|
764
|
+
function resolveFlags(cfg) {
|
|
765
|
+
if (cfg === false) {
|
|
766
|
+
return { appOpen: false, sessionStart: false, appBackground: false };
|
|
767
|
+
}
|
|
768
|
+
if (cfg === true || cfg === void 0) {
|
|
769
|
+
return { appOpen: true, sessionStart: true, appBackground: true };
|
|
770
|
+
}
|
|
771
|
+
return {
|
|
772
|
+
appOpen: cfg.appOpen ?? true,
|
|
773
|
+
sessionStart: cfg.sessionStart ?? true,
|
|
774
|
+
appBackground: cfg.appBackground ?? true
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
function installAutoTrack(args) {
|
|
778
|
+
const flags = resolveFlags(args.config.autoTrack);
|
|
779
|
+
const idleMs = args.config.sessionIdleMs ?? DEFAULT_IDLE_MS;
|
|
780
|
+
let lastActivity = Date.now();
|
|
781
|
+
const cleanups = [];
|
|
782
|
+
function newSessionId() {
|
|
783
|
+
return `sess_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
784
|
+
}
|
|
785
|
+
function ensureSession(emitStart) {
|
|
786
|
+
const now = Date.now();
|
|
787
|
+
let id = args.getSessionId();
|
|
788
|
+
if (!id || now - lastActivity > idleMs) {
|
|
789
|
+
id = newSessionId();
|
|
790
|
+
args.setSessionId(id);
|
|
791
|
+
if (emitStart && flags.sessionStart) {
|
|
792
|
+
args.fire("session.start", { sessionId: id });
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
lastActivity = now;
|
|
796
|
+
return id;
|
|
797
|
+
}
|
|
798
|
+
const sessionId = ensureSession(
|
|
799
|
+
/* emitStart */
|
|
800
|
+
flags.sessionStart
|
|
801
|
+
);
|
|
802
|
+
if (flags.appOpen) {
|
|
803
|
+
args.fire("app.opened", { sessionId });
|
|
804
|
+
}
|
|
805
|
+
if (flags.appBackground) {
|
|
806
|
+
let AppState = null;
|
|
807
|
+
try {
|
|
808
|
+
const dyn = globalThis.require;
|
|
809
|
+
const mod = dyn ? dyn("react-native") : null;
|
|
810
|
+
AppState = mod?.AppState ?? null;
|
|
811
|
+
} catch {
|
|
812
|
+
AppState = null;
|
|
813
|
+
}
|
|
814
|
+
if (AppState && typeof AppState.addEventListener === "function") {
|
|
815
|
+
const handler = (state) => {
|
|
816
|
+
ensureSession(true);
|
|
817
|
+
if (state === "background" || state === "inactive") {
|
|
818
|
+
args.fire("app.backgrounded", { sessionId: args.getSessionId() });
|
|
819
|
+
} else if (state === "active") {
|
|
820
|
+
args.fire("app.foregrounded", { sessionId: args.getSessionId() });
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
const sub = AppState.addEventListener("change", handler);
|
|
824
|
+
cleanups.push(() => {
|
|
825
|
+
if (typeof sub === "function") {
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
if (sub && typeof sub.remove === "function") {
|
|
829
|
+
sub.remove();
|
|
830
|
+
}
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
return () => {
|
|
835
|
+
for (const fn of cleanups) {
|
|
836
|
+
try {
|
|
837
|
+
fn();
|
|
838
|
+
} catch {
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
|
|
762
844
|
// src/index.ts
|
|
763
|
-
var SDK_VERSION = "0.
|
|
845
|
+
var SDK_VERSION = "0.11.0";
|
|
764
846
|
var DEFAULT_API_URL = "https://api.sendoracloud.com";
|
|
765
847
|
var ANON_KEY = "anon_id";
|
|
766
848
|
var USER_ID_KEY = "user_id";
|
|
@@ -824,6 +906,8 @@ var SendoraSDK = class {
|
|
|
824
906
|
this.userId = null;
|
|
825
907
|
this.consentGranted = true;
|
|
826
908
|
this.debug = false;
|
|
909
|
+
this.sessionId = "";
|
|
910
|
+
this.autoTrackCleanup = null;
|
|
827
911
|
}
|
|
828
912
|
/** Initialize the SDK. Must be awaited at app startup before identify/track. */
|
|
829
913
|
async init(config) {
|
|
@@ -898,6 +982,20 @@ var SendoraSDK = class {
|
|
|
898
982
|
});
|
|
899
983
|
this.auth.hydrate();
|
|
900
984
|
this.ready = true;
|
|
985
|
+
if (config.autoTrack !== false) {
|
|
986
|
+
this.autoTrackCleanup = installAutoTrack({
|
|
987
|
+
config: this.config,
|
|
988
|
+
fire: (eventType, properties) => this.track(eventType, properties),
|
|
989
|
+
getSessionId: () => this.sessionId,
|
|
990
|
+
setSessionId: (id) => {
|
|
991
|
+
this.sessionId = id;
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
/** Current logical session id, if auto-track is enabled. */
|
|
997
|
+
getSessionId() {
|
|
998
|
+
return this.sessionId;
|
|
901
999
|
}
|
|
902
1000
|
/** Stable anonymous identifier for this install — survives app restarts via AsyncStorage. */
|
|
903
1001
|
getAnonymousId() {
|
|
@@ -997,6 +1095,28 @@ var SendoraSDK = class {
|
|
|
997
1095
|
}
|
|
998
1096
|
return { tokenId: data.tokenId, created: data.created === true };
|
|
999
1097
|
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Push namespace — mirrors the iOS / Android / Web SDK shape so
|
|
1100
|
+
* `sendora.push.registerToken(...)` works consistently across all 5 SDKs.
|
|
1101
|
+
* Backed by the same registerPushToken() implementation; kept top-level
|
|
1102
|
+
* too for backwards compatibility with the s47 release.
|
|
1103
|
+
*/
|
|
1104
|
+
get push() {
|
|
1105
|
+
const self = this;
|
|
1106
|
+
return {
|
|
1107
|
+
registerToken: (reg) => self.registerPushToken(reg),
|
|
1108
|
+
trackOpen: async (sendId, clickAction) => {
|
|
1109
|
+
if (!self.config) throw new Error("[sendoracloud] init() must complete before push.trackOpen().");
|
|
1110
|
+
const body = { sendId };
|
|
1111
|
+
if (clickAction !== void 0) body.clickAction = clickAction;
|
|
1112
|
+
await post(
|
|
1113
|
+
{ apiUrl: self.config.apiUrl, publicKey: self.config.publicKey, debug: self.debug },
|
|
1114
|
+
"/push/track-open",
|
|
1115
|
+
body
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1000
1120
|
/**
|
|
1001
1121
|
* Clear identity + rotate the anonymous id. Call on logout. Awaits
|
|
1002
1122
|
* the AsyncStorage writes so a caller who kills the app immediately
|
|
@@ -1005,9 +1125,17 @@ var SendoraSDK = class {
|
|
|
1005
1125
|
async reset() {
|
|
1006
1126
|
this.userId = null;
|
|
1007
1127
|
this.anonId = uuid();
|
|
1128
|
+
this.sessionId = "";
|
|
1008
1129
|
await this.storage.clearAll();
|
|
1009
1130
|
this.storage.set(ANON_KEY, this.anonId);
|
|
1010
1131
|
}
|
|
1132
|
+
/** Tear down auto-track listeners. Call in test harnesses. */
|
|
1133
|
+
destroy() {
|
|
1134
|
+
if (this.autoTrackCleanup) {
|
|
1135
|
+
this.autoTrackCleanup();
|
|
1136
|
+
this.autoTrackCleanup = null;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1011
1139
|
async sendEvent(kind, payload) {
|
|
1012
1140
|
if (!this.config) return;
|
|
1013
1141
|
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.12.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",
|