rampkit-expo-dev 0.0.19 → 0.0.22

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/build/RampKit.js CHANGED
@@ -1,38 +1,73 @@
1
1
  "use strict";
2
+ /**
3
+ * RampKit Core SDK
4
+ * Main SDK class for RampKit Expo integration
5
+ */
2
6
  Object.defineProperty(exports, "__esModule", { value: true });
3
7
  exports.RampKitCore = void 0;
8
+ const react_native_1 = require("react-native");
4
9
  const RampkitOverlay_1 = require("./RampkitOverlay");
5
10
  const userId_1 = require("./userId");
11
+ const DeviceInfoCollector_1 = require("./DeviceInfoCollector");
12
+ const EventManager_1 = require("./EventManager");
13
+ const constants_1 = require("./constants");
6
14
  class RampKitCore {
7
15
  constructor() {
8
- this.config = {};
16
+ this.config = null;
9
17
  this.onboardingData = null;
10
18
  this.userId = null;
11
19
  this.appId = null;
20
+ this.deviceInfo = null;
21
+ this.appStateSubscription = null;
22
+ this.lastAppState = "active";
23
+ this.initialized = false;
12
24
  }
13
25
  static get instance() {
14
26
  if (!this._instance)
15
27
  this._instance = new RampKitCore();
16
28
  return this._instance;
17
29
  }
30
+ /**
31
+ * Initialize the RampKit SDK
32
+ */
18
33
  async init(config) {
19
34
  this.config = config;
20
35
  this.appId = config.appId;
21
36
  this.onOnboardingFinished = config.onOnboardingFinished;
22
37
  this.onShowPaywall = config.onShowPaywall || config.showPaywall;
23
38
  try {
24
- // Ensure a stable, encrypted user id exists on first init
25
- this.userId = await (0, userId_1.getRampKitUserId)();
39
+ // Step 1: Collect device info (includes user ID generation)
40
+ console.log("[RampKit] Init: Collecting device info...");
41
+ this.deviceInfo = await (0, DeviceInfoCollector_1.collectDeviceInfo)();
42
+ this.userId = this.deviceInfo.appUserId;
26
43
  console.log("[RampKit] Init: userId", this.userId);
44
+ // Step 2: Send device info to /app-users endpoint
45
+ console.log("[RampKit] Init: Sending user data to backend...");
46
+ await this.sendUserDataToBackend(this.deviceInfo);
47
+ // Step 3: Initialize event manager
48
+ console.log("[RampKit] Init: Initializing event manager...");
49
+ EventManager_1.eventManager.initialize(config.appId, this.deviceInfo);
50
+ // Step 4: Track app session started
51
+ EventManager_1.eventManager.trackAppSessionStarted(this.deviceInfo.isFirstLaunch, this.deviceInfo.launchCount);
52
+ // Step 5: Setup app state listener for background/foreground tracking
53
+ this.setupAppStateListener();
54
+ this.initialized = true;
27
55
  }
28
56
  catch (e) {
29
- console.log("[RampKit] Init: failed to resolve user id", e);
57
+ console.log("[RampKit] Init: Failed to initialize device info", e);
58
+ // Fallback to just getting user ID
59
+ try {
60
+ this.userId = await (0, userId_1.getRampKitUserId)();
61
+ }
62
+ catch (e2) {
63
+ console.log("[RampKit] Init: Failed to resolve user id", e2);
64
+ }
30
65
  }
31
- console.log("[RampKit] Init: starting onboarding load");
66
+ // Load onboarding data
67
+ console.log("[RampKit] Init: Starting onboarding load...");
32
68
  try {
33
- // First, fetch the app manifest to get the onboarding URL
34
- const manifestUrl = `${RampKitCore.MANIFEST_BASE_URL}/${config.appId}/manifest.json`;
35
- console.log("[RampKit] Init: fetching manifest from", manifestUrl);
69
+ const manifestUrl = `${constants_1.MANIFEST_BASE_URL}/${config.appId}/manifest.json`;
70
+ console.log("[RampKit] Init: Fetching manifest from", manifestUrl);
36
71
  const manifestResponse = await globalThis.fetch(manifestUrl);
37
72
  const manifest = await manifestResponse.json();
38
73
  if (!manifest.onboardings || manifest.onboardings.length === 0) {
@@ -40,41 +75,105 @@ class RampKitCore {
40
75
  }
41
76
  // Use the first onboarding
42
77
  const firstOnboarding = manifest.onboardings[0];
43
- console.log("[RampKit] Init: using onboarding", firstOnboarding.name, firstOnboarding.id);
78
+ console.log("[RampKit] Init: Using onboarding", firstOnboarding.name, firstOnboarding.id);
44
79
  // Fetch the actual onboarding data
45
80
  const onboardingResponse = await globalThis.fetch(firstOnboarding.url);
46
81
  const json = await onboardingResponse.json();
47
82
  this.onboardingData = json;
48
- try {
49
- console.log("[RampKit] Init: onboardingId", json && json.onboardingId);
50
- }
51
- catch (_) { }
52
- console.log("[RampKit] Init: onboarding loaded");
83
+ console.log("[RampKit] Init: onboardingId", json && json.onboardingId);
84
+ console.log("[RampKit] Init: Onboarding loaded");
53
85
  }
54
86
  catch (error) {
55
- console.log("[RampKit] Init: onboarding load failed", error);
87
+ console.log("[RampKit] Init: Onboarding load failed", error);
56
88
  this.onboardingData = null;
57
89
  }
58
- console.log("[RampKit] Init: finished", config);
90
+ console.log("[RampKit] Init: Finished", config);
59
91
  // Optionally auto-show onboarding overlay
60
92
  try {
61
93
  if (this.onboardingData && config.autoShowOnboarding) {
62
- console.log("[RampKit] Init: auto-show onboarding");
94
+ console.log("[RampKit] Init: Auto-show onboarding");
63
95
  this.showOnboarding();
64
96
  }
65
97
  }
66
98
  catch (_) { }
67
99
  }
100
+ /**
101
+ * Send user/device data to the /app-users endpoint
102
+ */
103
+ async sendUserDataToBackend(deviceInfo) {
104
+ try {
105
+ const url = `${constants_1.ENDPOINTS.BASE_URL}${constants_1.ENDPOINTS.APP_USERS}?appId=${this.appId}`;
106
+ const response = await fetch(url, {
107
+ method: "POST",
108
+ headers: {
109
+ "Content-Type": "application/json",
110
+ apikey: constants_1.SUPABASE_ANON_KEY,
111
+ Authorization: `Bearer ${constants_1.SUPABASE_ANON_KEY}`,
112
+ },
113
+ body: JSON.stringify(deviceInfo),
114
+ });
115
+ if (!response.ok) {
116
+ console.warn("[RampKit] Init: Failed to send user data:", response.status);
117
+ }
118
+ else {
119
+ console.log("[RampKit] Init: User data sent successfully");
120
+ }
121
+ }
122
+ catch (error) {
123
+ console.warn("[RampKit] Init: Error sending user data:", error);
124
+ }
125
+ }
126
+ /**
127
+ * Setup app state listener for background/foreground tracking
128
+ */
129
+ setupAppStateListener() {
130
+ this.appStateSubscription = react_native_1.AppState.addEventListener("change", (nextAppState) => {
131
+ if (this.lastAppState === "active" &&
132
+ (nextAppState === "background" || nextAppState === "inactive")) {
133
+ // App went to background
134
+ const sessionDuration = (0, DeviceInfoCollector_1.getSessionDurationSeconds)();
135
+ EventManager_1.eventManager.trackAppBackgrounded(sessionDuration);
136
+ }
137
+ else if ((this.lastAppState === "background" ||
138
+ this.lastAppState === "inactive") &&
139
+ nextAppState === "active") {
140
+ // App came to foreground
141
+ EventManager_1.eventManager.trackAppForegrounded();
142
+ }
143
+ this.lastAppState = nextAppState;
144
+ });
145
+ }
146
+ /**
147
+ * Get the onboarding data
148
+ */
68
149
  getOnboardingData() {
69
150
  return this.onboardingData;
70
151
  }
152
+ /**
153
+ * Get the user ID
154
+ */
71
155
  getUserId() {
72
156
  return this.userId;
73
157
  }
158
+ /**
159
+ * Get the device info
160
+ */
161
+ getDeviceInfo() {
162
+ return this.deviceInfo;
163
+ }
164
+ /**
165
+ * Check if SDK is initialized
166
+ */
167
+ isInitialized() {
168
+ return this.initialized;
169
+ }
170
+ /**
171
+ * Show the onboarding overlay
172
+ */
74
173
  showOnboarding(opts) {
75
174
  const data = this.onboardingData;
76
175
  if (!data || !Array.isArray(data.screens) || data.screens.length === 0) {
77
- console.log("[RampKit] ShowOnboarding: no onboarding data available");
176
+ console.log("[RampKit] ShowOnboarding: No onboarding data available");
78
177
  return;
79
178
  }
80
179
  try {
@@ -95,17 +194,20 @@ class RampKitCore {
95
194
  const screens = data.screens.map((s) => ({
96
195
  id: s.id,
97
196
  html: s.html ||
98
- `<div style=\"padding:24px\"><h1>${s.label || s.id}</h1><button onclick=\"window.ReactNativeWebView && window.ReactNativeWebView.postMessage('rampkit:tap')\">Continue</button></div>`,
197
+ `<div style="padding:24px"><h1>${s.label || s.id}</h1><button onclick="window.ReactNativeWebView && window.ReactNativeWebView.postMessage('rampkit:tap')">Continue</button></div>`,
99
198
  css: s.css,
100
199
  js: s.js,
101
200
  }));
102
201
  const requiredScripts = Array.isArray(data.requiredScripts)
103
202
  ? data.requiredScripts
104
203
  : [];
204
+ // Track onboarding started event
205
+ const onboardingId = data.onboardingId || data.id || "unknown";
206
+ EventManager_1.eventManager.trackOnboardingStarted(onboardingId, screens.length);
105
207
  // Optional warm-up
106
208
  try {
107
209
  (0, RampkitOverlay_1.preloadRampkitOverlay)({
108
- onboardingId: data.onboardingId,
210
+ onboardingId,
109
211
  screens,
110
212
  variables,
111
213
  requiredScripts,
@@ -113,27 +215,114 @@ class RampKitCore {
113
215
  }
114
216
  catch (_) { }
115
217
  (0, RampkitOverlay_1.showRampkitOverlay)({
116
- onboardingId: data.onboardingId,
218
+ onboardingId,
117
219
  screens,
118
220
  variables,
119
221
  requiredScripts,
120
222
  onOnboardingFinished: (payload) => {
121
223
  var _a;
224
+ // Track onboarding completed
225
+ EventManager_1.eventManager.trackOnboardingCompleted(screens.length, screens.length, onboardingId);
122
226
  try {
123
227
  (_a = this.onOnboardingFinished) === null || _a === void 0 ? void 0 : _a.call(this, payload);
124
228
  }
125
229
  catch (_) { }
126
230
  },
127
231
  onShowPaywall: (opts === null || opts === void 0 ? void 0 : opts.onShowPaywall) || (opts === null || opts === void 0 ? void 0 : opts.showPaywall) || this.onShowPaywall,
232
+ onScreenChange: (screenIndex, screenId) => {
233
+ // Track screen view within onboarding
234
+ EventManager_1.eventManager.trackOnboardingScreenViewed(screenId, screenIndex, screens.length, onboardingId);
235
+ },
236
+ onOnboardingAbandoned: (reason, lastScreenIndex, lastScreenId) => {
237
+ // Track onboarding abandoned
238
+ EventManager_1.eventManager.trackOnboardingAbandoned(reason, lastScreenId, onboardingId);
239
+ },
240
+ onNotificationPermissionRequested: () => {
241
+ EventManager_1.eventManager.trackNotificationsPromptShown();
242
+ },
243
+ onNotificationPermissionResult: (granted) => {
244
+ EventManager_1.eventManager.trackNotificationsResponse(granted ? "granted" : "denied");
245
+ },
128
246
  });
129
247
  }
130
248
  catch (e) {
131
- console.log("[RampKit] ShowOnboarding: failed to show overlay", e);
249
+ console.log("[RampKit] ShowOnboarding: Failed to show overlay", e);
132
250
  }
133
251
  }
252
+ /**
253
+ * Close the onboarding overlay
254
+ */
134
255
  closeOnboarding() {
135
256
  (0, RampkitOverlay_1.closeRampkitOverlay)();
136
257
  }
258
+ // ============================================================================
259
+ // Public Event Tracking API
260
+ // ============================================================================
261
+ /**
262
+ * Track a custom event
263
+ */
264
+ trackEvent(eventName, properties = {}, context) {
265
+ EventManager_1.eventManager.track(eventName, properties, context);
266
+ }
267
+ /**
268
+ * Track a screen view
269
+ */
270
+ trackScreenView(screenName, referrer) {
271
+ EventManager_1.eventManager.trackScreenView(screenName, referrer);
272
+ }
273
+ /**
274
+ * Track a CTA tap
275
+ */
276
+ trackCtaTap(buttonId, buttonText) {
277
+ EventManager_1.eventManager.trackCtaTap(buttonId, buttonText);
278
+ }
279
+ /**
280
+ * Track paywall shown
281
+ */
282
+ trackPaywallShown(paywallId, placement, products) {
283
+ EventManager_1.eventManager.trackPaywallShown(paywallId, placement, products);
284
+ }
285
+ /**
286
+ * Track paywall primary action tap
287
+ */
288
+ trackPaywallPrimaryActionTap(paywallId, productId) {
289
+ EventManager_1.eventManager.trackPaywallPrimaryActionTap(paywallId, productId);
290
+ }
291
+ /**
292
+ * Track paywall closed
293
+ */
294
+ trackPaywallClosed(paywallId, reason) {
295
+ EventManager_1.eventManager.trackPaywallClosed(paywallId, reason);
296
+ }
297
+ /**
298
+ * Track purchase started
299
+ */
300
+ trackPurchaseStarted(productId, amount, currency) {
301
+ EventManager_1.eventManager.trackPurchaseStarted(productId, amount, currency);
302
+ }
303
+ /**
304
+ * Track purchase completed
305
+ */
306
+ trackPurchaseCompleted(properties) {
307
+ EventManager_1.eventManager.trackPurchaseCompleted(properties);
308
+ }
309
+ /**
310
+ * Track purchase failed
311
+ */
312
+ trackPurchaseFailed(productId, errorCode, errorMessage) {
313
+ EventManager_1.eventManager.trackPurchaseFailed(productId, errorCode, errorMessage);
314
+ }
315
+ /**
316
+ * Cleanup SDK resources
317
+ */
318
+ cleanup() {
319
+ if (this.appStateSubscription) {
320
+ this.appStateSubscription.remove();
321
+ this.appStateSubscription = null;
322
+ }
323
+ EventManager_1.eventManager.reset();
324
+ (0, DeviceInfoCollector_1.resetSession)();
325
+ this.initialized = false;
326
+ }
137
327
  }
138
328
  exports.RampKitCore = RampKitCore;
139
- RampKitCore.MANIFEST_BASE_URL = "https://dh1psiwzzzkgr.cloudfront.net";
@@ -0,0 +1,151 @@
1
+ /**
2
+ * RampKit Native Module Bridge
3
+ * TypeScript interface to the native iOS/Android module
4
+ */
5
+ interface RampKitNativeModule {
6
+ getDeviceInfo(): Promise<NativeDeviceInfo>;
7
+ getUserId(): Promise<string>;
8
+ getStoredValue(key: string): Promise<string | null>;
9
+ setStoredValue(key: string, value: string): Promise<void>;
10
+ getLaunchTrackingData(): Promise<NativeLaunchData>;
11
+ impactAsync(style: string): Promise<void>;
12
+ notificationAsync(type: string): Promise<void>;
13
+ selectionAsync(): Promise<void>;
14
+ requestReview(): Promise<boolean | void>;
15
+ isReviewAvailable(): Promise<boolean>;
16
+ getStoreUrl(): Promise<string | null>;
17
+ requestNotificationPermissions(options?: NotificationOptions): Promise<NotificationPermissionResult>;
18
+ getNotificationPermissions(): Promise<NotificationPermissionResult>;
19
+ }
20
+ export interface NativeDeviceInfo {
21
+ appUserId: string;
22
+ vendorId: string | null;
23
+ appSessionId: string;
24
+ installDate: string;
25
+ isFirstLaunch: boolean;
26
+ launchCount: number;
27
+ lastLaunchAt: string | null;
28
+ bundleId: string | null;
29
+ appName: string | null;
30
+ appVersion: string | null;
31
+ buildNumber: string | null;
32
+ platform: string;
33
+ platformVersion: string;
34
+ deviceModel: string;
35
+ deviceName: string;
36
+ isSimulator: boolean;
37
+ deviceLanguageCode: string | null;
38
+ deviceLocale: string;
39
+ regionCode: string | null;
40
+ preferredLanguage: string | null;
41
+ preferredLanguages: string[];
42
+ deviceCurrencyCode: string | null;
43
+ deviceCurrencySymbol: string | null;
44
+ timezoneIdentifier: string;
45
+ timezoneOffsetSeconds: number;
46
+ interfaceStyle: string;
47
+ screenWidth: number;
48
+ screenHeight: number;
49
+ screenScale: number;
50
+ isLowPowerMode: boolean;
51
+ totalMemoryBytes: number;
52
+ collectedAt: string;
53
+ }
54
+ export interface NativeLaunchData {
55
+ installDate: string;
56
+ isFirstLaunch: boolean;
57
+ launchCount: number;
58
+ lastLaunchAt: string | null;
59
+ }
60
+ export interface NotificationOptions {
61
+ ios?: {
62
+ allowAlert?: boolean;
63
+ allowBadge?: boolean;
64
+ allowSound?: boolean;
65
+ };
66
+ android?: {
67
+ channelId?: string;
68
+ name?: string;
69
+ importance?: "MAX" | "HIGH" | "DEFAULT" | "LOW" | "MIN";
70
+ };
71
+ }
72
+ export interface NotificationPermissionResult {
73
+ granted: boolean;
74
+ status: "undetermined" | "denied" | "granted" | "provisional" | "ephemeral";
75
+ canAskAgain: boolean;
76
+ ios?: {
77
+ alertSetting?: string;
78
+ badgeSetting?: string;
79
+ soundSetting?: string;
80
+ lockScreenSetting?: string;
81
+ notificationCenterSetting?: string;
82
+ };
83
+ error?: string;
84
+ }
85
+ export type ImpactStyle = "light" | "medium" | "heavy" | "rigid" | "soft";
86
+ export type NotificationType = "success" | "warning" | "error";
87
+ declare let RampKitNativeModule: RampKitNativeModule;
88
+ export default RampKitNativeModule;
89
+ export declare function getDeviceInfo(): Promise<NativeDeviceInfo>;
90
+ export declare function getUserId(): Promise<string>;
91
+ export declare function getStoredValue(key: string): Promise<string | null>;
92
+ export declare function setStoredValue(key: string, value: string): Promise<void>;
93
+ export declare function getLaunchTrackingData(): Promise<NativeLaunchData>;
94
+ export declare const Haptics: {
95
+ /**
96
+ * Trigger an impact haptic feedback
97
+ */
98
+ impactAsync(style?: ImpactStyle): Promise<void>;
99
+ /**
100
+ * Trigger a notification haptic feedback
101
+ */
102
+ notificationAsync(type?: NotificationType): Promise<void>;
103
+ /**
104
+ * Trigger a selection haptic feedback
105
+ */
106
+ selectionAsync(): Promise<void>;
107
+ };
108
+ export declare const StoreReview: {
109
+ /**
110
+ * Request an in-app review
111
+ */
112
+ requestReview(): Promise<void>;
113
+ /**
114
+ * Check if in-app review is available
115
+ */
116
+ isAvailableAsync(): Promise<boolean>;
117
+ /**
118
+ * Check if the review action is available
119
+ */
120
+ hasAction(): Promise<boolean>;
121
+ /**
122
+ * Get the store URL for the app
123
+ */
124
+ storeUrl(): string | null;
125
+ };
126
+ export declare const Notifications: {
127
+ /**
128
+ * Request notification permissions
129
+ */
130
+ requestPermissionsAsync(options?: NotificationOptions): Promise<NotificationPermissionResult>;
131
+ /**
132
+ * Get current notification permissions
133
+ */
134
+ getPermissionsAsync(): Promise<NotificationPermissionResult>;
135
+ /**
136
+ * Set notification handler (no-op in native implementation)
137
+ * The app should handle this separately if needed
138
+ */
139
+ setNotificationHandler(_handler: any): void;
140
+ /**
141
+ * Android notification channel creation is handled in requestPermissionsAsync
142
+ */
143
+ setNotificationChannelAsync(_channelId: string, _options: any): Promise<void>;
144
+ AndroidImportance: {
145
+ MAX: number;
146
+ HIGH: number;
147
+ DEFAULT: number;
148
+ LOW: number;
149
+ MIN: number;
150
+ };
151
+ };