rampkit-expo-dev 0.0.57 → 0.0.60

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
@@ -38,10 +38,11 @@ npx expo install rampkit-expo-dev
38
38
  ```ts
39
39
  import { RampKit } from "rampkit-expo-dev";
40
40
 
41
- await RampKit.init({
42
- apiKey: "YOUR_API_KEY",
41
+ await RampKit.configure({
42
+ appId: "YOUR_APP_ID",
43
43
  environment: "production", // optional
44
44
  autoShowOnboarding: false, // optional
45
+ appUserID: "your-user-123", // optional - your own user ID for analytics linking
45
46
  onOnboardingFinished: (payload) => {
46
47
  // optional callback fired when the flow finishes
47
48
  },
@@ -59,23 +60,82 @@ RampKit.showOnboarding();
59
60
  ## Configuration Options
60
61
 
61
62
  ```ts
62
- await RampKit.init({
63
- apiKey: "abc123",
63
+ await RampKit.configure({
64
+ appId: "YOUR_APP_ID",
64
65
  environment: "staging", // optional
65
- autoShowOnboarding: true, // optional (auto-present after init if data is available)
66
+ autoShowOnboarding: true, // optional (auto-present after configure if data is available)
67
+ appUserID: "user-123", // optional - link with your own user database
66
68
  onOnboardingFinished: (payload) => {
67
69
  // optional
68
70
  },
69
71
  });
70
72
 
71
73
  // Access the generated stable user id (stored securely)
72
- const idFromInit = RampKit.getUserId(); // string | null (available after init)
74
+ const idFromInit = RampKit.getUserId(); // string | null (available after configure)
73
75
 
74
76
  // Or fetch/generate it directly (always returns a string)
75
77
  import { getRampKitUserId } from "rampkit-expo-dev";
76
78
  const userId = await getRampKitUserId();
79
+
80
+ // Get stored onboarding responses
81
+ const responses = await RampKit.getOnboardingResponses();
82
+ ```
83
+
84
+ ---
85
+
86
+ ## Custom App User ID
87
+
88
+ You can associate your own user identifier with RampKit analytics. This is useful for linking RampKit data with your own user database.
89
+
90
+ **Important:** This is an alias only - RampKit still generates and uses its own internal user ID (`appUserId`) for tracking. Your custom `appUserID` is stored alongside it for your reference.
91
+
92
+ ### Set at Configuration
93
+
94
+ ```ts
95
+ await RampKit.configure({
96
+ appId: "YOUR_APP_ID",
97
+ appUserID: "your-user-123", // Set during configuration
98
+ });
99
+ ```
100
+
101
+ ### Set After Configuration
102
+
103
+ ```ts
104
+ // Set or update the app user ID at any time
105
+ await RampKit.setAppUserID("your-user-123");
106
+
107
+ // Get the current app user ID
108
+ const customId = RampKit.getAppUserID(); // string | null
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Accessing Onboarding Responses
114
+
115
+ RampKit automatically stores user responses when questions are answered during onboarding. You can retrieve these responses at any time:
116
+
117
+ ```ts
118
+ const responses = await RampKit.getOnboardingResponses();
119
+
120
+ for (const response of responses) {
121
+ console.log("Question:", response.questionId);
122
+ console.log("Answer:", response.answer);
123
+ console.log("Answered at:", response.answeredAt);
124
+ }
77
125
  ```
78
126
 
127
+ Each `OnboardingResponse` contains:
128
+
129
+ | Property | Type | Description |
130
+ |----------|------|-------------|
131
+ | `questionId` | `string` | Unique identifier for the question |
132
+ | `answer` | `any` | The user's answer (any JSON-serializable value) |
133
+ | `questionText` | `string?` | Optional question text shown to user |
134
+ | `screenName` | `string?` | Screen where question was answered |
135
+ | `answeredAt` | `string` | ISO 8601 timestamp |
136
+
137
+ Responses are automatically cleared when `reset()` is called.
138
+
79
139
  More examples are in the docs:
80
140
  https://rampkit.com/docs
81
141
 
@@ -90,8 +150,8 @@ import { Button } from "react-native";
90
150
 
91
151
  export default function App() {
92
152
  useEffect(() => {
93
- // Initialize once in your app
94
- RampKit.init({ apiKey: "YOUR_API_KEY" });
153
+ // Configure once in your app
154
+ RampKit.configure({ appId: "YOUR_APP_ID" });
95
155
  }, []);
96
156
 
97
157
  return (
@@ -46,6 +46,7 @@ async function collectDeviceInfo() {
46
46
  const deviceInfo = {
47
47
  // User & Session Identifiers
48
48
  appUserId: nativeInfo.appUserId,
49
+ appUserID: null, // Custom app user ID is set separately via config or setAppUserID()
49
50
  vendorId: nativeInfo.vendorId,
50
51
  appSessionId: nativeInfo.appSessionId,
51
52
  // Launch Tracking
@@ -141,6 +142,7 @@ function getFallbackDeviceInfo() {
141
142
  sessionStartTime = new Date();
142
143
  return {
143
144
  appUserId: fallbackUserId,
145
+ appUserID: null, // Custom app user ID is set separately via config or setAppUserID()
144
146
  vendorId: null,
145
147
  appSessionId: sessionId,
146
148
  installDate: now,
@@ -174,14 +174,23 @@ class EventManager {
174
174
  body: JSON.stringify(event),
175
175
  });
176
176
  if (!response.ok) {
177
- console.warn("[RampKit] EventManager: Failed to send event:", event.eventName, response.status);
177
+ // Try to get error details from response body
178
+ let errorDetails = "";
179
+ try {
180
+ const errorBody = await response.text();
181
+ errorDetails = errorBody ? ` - ${errorBody}` : "";
182
+ }
183
+ catch (_a) {
184
+ // Ignore if we can't read the body
185
+ }
186
+ console.warn(`[RampKit] EventManager: Failed to send event: ${event.eventName}`, `\n Status: ${response.status} ${response.statusText}`, `\n URL: ${url}`, `\n AppId: ${event.appId}`, `\n UserId: ${event.appUserId}`, errorDetails ? `\n Error: ${errorDetails}` : "");
178
187
  }
179
188
  else {
180
189
  console.log("[RampKit] EventManager: Event sent:", event.eventName);
181
190
  }
182
191
  }
183
192
  catch (error) {
184
- console.warn("[RampKit] EventManager: Error sending event:", event.eventName, error);
193
+ console.warn(`[RampKit] EventManager: Network error sending event: ${event.eventName}`, `\n Error: ${error instanceof Error ? error.message : String(error)}`);
185
194
  }
186
195
  }
187
196
  // ============================================================================
@@ -0,0 +1,24 @@
1
+ /**
2
+ * OnboardingResponseStorage
3
+ * Manages persistent storage of onboarding responses
4
+ */
5
+ import { OnboardingResponse } from "./types";
6
+ /**
7
+ * Manages persistent storage of onboarding responses
8
+ */
9
+ export declare const OnboardingResponseStorage: {
10
+ /**
11
+ * Save a new response, merging with existing responses
12
+ * If a response with the same questionId exists, it will be updated
13
+ */
14
+ saveResponse(response: OnboardingResponse): Promise<void>;
15
+ /**
16
+ * Retrieve all stored responses
17
+ * @returns Array of OnboardingResponse objects, empty array if none found
18
+ */
19
+ retrieveResponses(): Promise<OnboardingResponse[]>;
20
+ /**
21
+ * Clear all stored responses
22
+ */
23
+ clearResponses(): Promise<void>;
24
+ };
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ /**
3
+ * OnboardingResponseStorage
4
+ * Manages persistent storage of onboarding responses
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.OnboardingResponseStorage = void 0;
8
+ const RampKitNative_1 = require("./RampKitNative");
9
+ const constants_1 = require("./constants");
10
+ /**
11
+ * Manages persistent storage of onboarding responses
12
+ */
13
+ exports.OnboardingResponseStorage = {
14
+ /**
15
+ * Save a new response, merging with existing responses
16
+ * If a response with the same questionId exists, it will be updated
17
+ */
18
+ async saveResponse(response) {
19
+ try {
20
+ const responses = await this.retrieveResponses();
21
+ // Update existing response for same questionId or append new
22
+ const existingIndex = responses.findIndex((r) => r.questionId === response.questionId);
23
+ if (existingIndex >= 0) {
24
+ responses[existingIndex] = response;
25
+ }
26
+ else {
27
+ responses.push(response);
28
+ }
29
+ // Serialize and save
30
+ await (0, RampKitNative_1.setStoredValue)(constants_1.STORAGE_KEYS.ONBOARDING_RESPONSES, JSON.stringify(responses));
31
+ if (__DEV__) {
32
+ console.log(`[RampKit] Saved response for question: ${response.questionId}`);
33
+ }
34
+ }
35
+ catch (error) {
36
+ console.warn("[RampKit] Failed to save onboarding response:", error);
37
+ }
38
+ },
39
+ /**
40
+ * Retrieve all stored responses
41
+ * @returns Array of OnboardingResponse objects, empty array if none found
42
+ */
43
+ async retrieveResponses() {
44
+ try {
45
+ const jsonString = await (0, RampKitNative_1.getStoredValue)(constants_1.STORAGE_KEYS.ONBOARDING_RESPONSES);
46
+ if (!jsonString) {
47
+ return [];
48
+ }
49
+ return JSON.parse(jsonString);
50
+ }
51
+ catch (error) {
52
+ console.warn("[RampKit] Failed to retrieve onboarding responses:", error);
53
+ return [];
54
+ }
55
+ },
56
+ /**
57
+ * Clear all stored responses
58
+ */
59
+ async clearResponses() {
60
+ try {
61
+ await (0, RampKitNative_1.setStoredValue)(constants_1.STORAGE_KEYS.ONBOARDING_RESPONSES, "");
62
+ if (__DEV__) {
63
+ console.log("[RampKit] Cleared all onboarding responses");
64
+ }
65
+ }
66
+ catch (error) {
67
+ console.warn("[RampKit] Failed to clear onboarding responses:", error);
68
+ }
69
+ },
70
+ };
@@ -2,7 +2,7 @@
2
2
  * RampKit Core SDK
3
3
  * Main SDK class for RampKit Expo integration
4
4
  */
5
- import { DeviceInfo, RampKitConfig, EventContext } from "./types";
5
+ import { DeviceInfo, RampKitConfig, EventContext, OnboardingResponse } from "./types";
6
6
  export declare class RampKitCore {
7
7
  private static _instance;
8
8
  private config;
@@ -15,11 +15,34 @@ export declare class RampKitCore {
15
15
  private appStateSubscription;
16
16
  private lastAppState;
17
17
  private initialized;
18
+ /** Custom App User ID provided by the developer (alias for their user system) */
19
+ private appUserID;
18
20
  static get instance(): RampKitCore;
19
21
  /**
20
- * Initialize the RampKit SDK
22
+ * Configure the RampKit SDK
23
+ * @param config Configuration options including appId, callbacks, and optional appUserID
24
+ */
25
+ configure(config: RampKitConfig): Promise<void>;
26
+ /**
27
+ * @deprecated Use `configure()` instead. This method will be removed in a future version.
21
28
  */
22
29
  init(config: RampKitConfig): Promise<void>;
30
+ /**
31
+ * Set a custom App User ID to associate with this user.
32
+ * This is an alias for your own user identification system.
33
+ *
34
+ * Note: This does NOT replace the RampKit-generated user ID (appUserId).
35
+ * RampKit will continue to use its own stable UUID for internal tracking.
36
+ * This custom ID is sent to the backend for you to correlate with your own user database.
37
+ *
38
+ * @param appUserID Your custom user identifier
39
+ */
40
+ setAppUserID(appUserID: string): Promise<void>;
41
+ /**
42
+ * Get the custom App User ID if one has been set.
43
+ * @returns The custom App User ID or null if not set
44
+ */
45
+ getAppUserID(): string | null;
23
46
  /**
24
47
  * Send user/device data to the /app-users endpoint
25
48
  */
@@ -44,6 +67,11 @@ export declare class RampKitCore {
44
67
  * Check if SDK is initialized
45
68
  */
46
69
  isInitialized(): boolean;
70
+ /**
71
+ * Get all stored onboarding responses
72
+ * @returns Promise resolving to array of OnboardingResponse objects
73
+ */
74
+ getOnboardingResponses(): Promise<OnboardingResponse[]>;
47
75
  /**
48
76
  * Show the onboarding overlay
49
77
  */
package/build/RampKit.js CHANGED
@@ -12,6 +12,7 @@ const DeviceInfoCollector_1 = require("./DeviceInfoCollector");
12
12
  const EventManager_1 = require("./EventManager");
13
13
  const RampKitNative_1 = require("./RampKitNative");
14
14
  const constants_1 = require("./constants");
15
+ const OnboardingResponseStorage_1 = require("./OnboardingResponseStorage");
15
16
  class RampKitCore {
16
17
  constructor() {
17
18
  this.config = null;
@@ -22,6 +23,8 @@ class RampKitCore {
22
23
  this.appStateSubscription = null;
23
24
  this.lastAppState = "active";
24
25
  this.initialized = false;
26
+ /** Custom App User ID provided by the developer (alias for their user system) */
27
+ this.appUserID = null;
25
28
  }
26
29
  static get instance() {
27
30
  if (!this._instance)
@@ -29,49 +32,60 @@ class RampKitCore {
29
32
  return this._instance;
30
33
  }
31
34
  /**
32
- * Initialize the RampKit SDK
35
+ * Configure the RampKit SDK
36
+ * @param config Configuration options including appId, callbacks, and optional appUserID
33
37
  */
34
- async init(config) {
38
+ async configure(config) {
35
39
  this.config = config;
36
40
  this.appId = config.appId;
37
41
  this.onOnboardingFinished = config.onOnboardingFinished;
38
42
  this.onShowPaywall = config.onShowPaywall || config.showPaywall;
43
+ // Store custom App User ID if provided (this is an alias, not the RampKit user ID)
44
+ if (config.appUserID) {
45
+ this.appUserID = config.appUserID;
46
+ console.log("[RampKit] Configure: appUserID set to", this.appUserID);
47
+ }
39
48
  try {
40
49
  // Step 1: Collect device info (includes user ID generation)
41
- console.log("[RampKit] Init: Collecting device info...");
42
- this.deviceInfo = await (0, DeviceInfoCollector_1.collectDeviceInfo)();
50
+ console.log("[RampKit] Configure: Collecting device info...");
51
+ const baseDeviceInfo = await (0, DeviceInfoCollector_1.collectDeviceInfo)();
52
+ // Add the custom appUserID to device info
53
+ this.deviceInfo = {
54
+ ...baseDeviceInfo,
55
+ appUserID: this.appUserID,
56
+ };
43
57
  this.userId = this.deviceInfo.appUserId;
44
- console.log("[RampKit] Init: userId", this.userId);
58
+ console.log("[RampKit] Configure: userId", this.userId);
45
59
  // Step 2: Send device info to /app-users endpoint
46
- console.log("[RampKit] Init: Sending user data to backend...");
60
+ console.log("[RampKit] Configure: Sending user data to backend...");
47
61
  await this.sendUserDataToBackend(this.deviceInfo);
48
62
  // Step 3: Initialize event manager
49
- console.log("[RampKit] Init: Initializing event manager...");
63
+ console.log("[RampKit] Configure: Initializing event manager...");
50
64
  EventManager_1.eventManager.initialize(config.appId, this.deviceInfo);
51
65
  // Step 4: Track app session started
52
66
  EventManager_1.eventManager.trackAppSessionStarted(this.deviceInfo.isFirstLaunch, this.deviceInfo.launchCount);
53
67
  // Step 5: Setup app state listener for background/foreground tracking
54
68
  this.setupAppStateListener();
55
69
  // Step 6: Start transaction observer for automatic purchase tracking
56
- console.log("[RampKit] Init: Starting transaction observer...");
70
+ console.log("[RampKit] Configure: Starting transaction observer...");
57
71
  await RampKitNative_1.TransactionObserver.start(config.appId);
58
72
  this.initialized = true;
59
73
  }
60
74
  catch (e) {
61
- console.log("[RampKit] Init: Failed to initialize device info", e);
75
+ console.log("[RampKit] Configure: Failed to initialize device info", e);
62
76
  // Fallback to just getting user ID
63
77
  try {
64
78
  this.userId = await (0, userId_1.getRampKitUserId)();
65
79
  }
66
80
  catch (e2) {
67
- console.log("[RampKit] Init: Failed to resolve user id", e2);
81
+ console.log("[RampKit] Configure: Failed to resolve user id", e2);
68
82
  }
69
83
  }
70
84
  // Load onboarding data
71
- console.log("[RampKit] Init: Starting onboarding load...");
85
+ console.log("[RampKit] Configure: Starting onboarding load...");
72
86
  try {
73
87
  const manifestUrl = `${constants_1.MANIFEST_BASE_URL}/${config.appId}/manifest.json`;
74
- console.log("[RampKit] Init: Fetching manifest from", manifestUrl);
88
+ console.log("[RampKit] Configure: Fetching manifest from", manifestUrl);
75
89
  const manifestResponse = await globalThis.fetch(manifestUrl);
76
90
  const manifest = await manifestResponse.json();
77
91
  if (!manifest.onboardings || manifest.onboardings.length === 0) {
@@ -79,28 +93,73 @@ class RampKitCore {
79
93
  }
80
94
  // Use the first onboarding
81
95
  const firstOnboarding = manifest.onboardings[0];
82
- console.log("[RampKit] Init: Using onboarding", firstOnboarding.name, firstOnboarding.id);
96
+ console.log("[RampKit] Configure: Using onboarding", firstOnboarding.name, firstOnboarding.id);
83
97
  // Fetch the actual onboarding data
84
98
  const onboardingResponse = await globalThis.fetch(firstOnboarding.url);
85
99
  const json = await onboardingResponse.json();
86
100
  this.onboardingData = json;
87
- console.log("[RampKit] Init: onboardingId", json && json.onboardingId);
88
- console.log("[RampKit] Init: Onboarding loaded");
101
+ console.log("[RampKit] Configure: onboardingId", json && json.onboardingId);
102
+ console.log("[RampKit] Configure: Onboarding loaded");
89
103
  }
90
104
  catch (error) {
91
- console.log("[RampKit] Init: Onboarding load failed", error);
105
+ console.log("[RampKit] Configure: Onboarding load failed", error);
92
106
  this.onboardingData = null;
93
107
  }
94
- console.log("[RampKit] Init: Finished", config);
108
+ console.log("[RampKit] Configure: Finished", config);
95
109
  // Optionally auto-show onboarding overlay
96
110
  try {
97
111
  if (this.onboardingData && config.autoShowOnboarding) {
98
- console.log("[RampKit] Init: Auto-show onboarding");
112
+ console.log("[RampKit] Configure: Auto-show onboarding");
99
113
  this.showOnboarding();
100
114
  }
101
115
  }
102
116
  catch (_) { }
103
117
  }
118
+ /**
119
+ * @deprecated Use `configure()` instead. This method will be removed in a future version.
120
+ */
121
+ async init(config) {
122
+ console.warn("[RampKit] init() is deprecated. Use configure() instead.");
123
+ return this.configure(config);
124
+ }
125
+ /**
126
+ * Set a custom App User ID to associate with this user.
127
+ * This is an alias for your own user identification system.
128
+ *
129
+ * Note: This does NOT replace the RampKit-generated user ID (appUserId).
130
+ * RampKit will continue to use its own stable UUID for internal tracking.
131
+ * This custom ID is sent to the backend for you to correlate with your own user database.
132
+ *
133
+ * @param appUserID Your custom user identifier
134
+ */
135
+ async setAppUserID(appUserID) {
136
+ this.appUserID = appUserID;
137
+ console.log("[RampKit] setAppUserID:", appUserID);
138
+ // Update device info with the new appUserID
139
+ if (this.deviceInfo) {
140
+ this.deviceInfo = {
141
+ ...this.deviceInfo,
142
+ appUserID: appUserID,
143
+ };
144
+ // Sync updated info to backend
145
+ if (this.initialized) {
146
+ try {
147
+ await this.sendUserDataToBackend(this.deviceInfo);
148
+ console.log("[RampKit] setAppUserID: Synced to backend");
149
+ }
150
+ catch (e) {
151
+ console.warn("[RampKit] setAppUserID: Failed to sync to backend", e);
152
+ }
153
+ }
154
+ }
155
+ }
156
+ /**
157
+ * Get the custom App User ID if one has been set.
158
+ * @returns The custom App User ID or null if not set
159
+ */
160
+ getAppUserID() {
161
+ return this.appUserID;
162
+ }
104
163
  /**
105
164
  * Send user/device data to the /app-users endpoint
106
165
  */
@@ -117,14 +176,23 @@ class RampKitCore {
117
176
  body: JSON.stringify(deviceInfo),
118
177
  });
119
178
  if (!response.ok) {
120
- console.warn("[RampKit] Init: Failed to send user data:", response.status);
179
+ // Try to get error details from response body
180
+ let errorDetails = "";
181
+ try {
182
+ const errorBody = await response.text();
183
+ errorDetails = errorBody ? ` - ${errorBody}` : "";
184
+ }
185
+ catch (_a) {
186
+ // Ignore if we can't read the body
187
+ }
188
+ console.warn(`[RampKit] Configure: Failed to send user data`, `\n Status: ${response.status} ${response.statusText}`, `\n URL: ${url}`, `\n AppId: ${this.appId}`, `\n UserId: ${deviceInfo.appUserId}`, errorDetails ? `\n Error: ${errorDetails}` : "");
121
189
  }
122
190
  else {
123
- console.log("[RampKit] Init: User data sent successfully");
191
+ console.log("[RampKit] Configure: User data sent successfully");
124
192
  }
125
193
  }
126
194
  catch (error) {
127
- console.warn("[RampKit] Init: Error sending user data:", error);
195
+ console.warn(`[RampKit] Configure: Network error sending user data`, `\n Error: ${error instanceof Error ? error.message : String(error)}`);
128
196
  }
129
197
  }
130
198
  /**
@@ -171,6 +239,13 @@ class RampKitCore {
171
239
  isInitialized() {
172
240
  return this.initialized;
173
241
  }
242
+ /**
243
+ * Get all stored onboarding responses
244
+ * @returns Promise resolving to array of OnboardingResponse objects
245
+ */
246
+ async getOnboardingResponses() {
247
+ return OnboardingResponseStorage_1.OnboardingResponseStorage.retrieveResponses();
248
+ }
174
249
  /**
175
250
  * Show the onboarding overlay
176
251
  */
@@ -323,9 +398,12 @@ class RampKitCore {
323
398
  this.deviceInfo = null;
324
399
  this.onboardingData = null;
325
400
  this.initialized = false;
401
+ this.appUserID = null;
402
+ // Clear stored onboarding responses
403
+ await OnboardingResponseStorage_1.OnboardingResponseStorage.clearResponses();
326
404
  console.log("[RampKit] Reset: Re-initializing SDK...");
327
405
  // Re-initialize with stored config
328
- await this.init(this.config);
406
+ await this.configure(this.config);
329
407
  }
330
408
  }
331
409
  exports.RampKitCore = RampKitCore;
@@ -48,6 +48,7 @@ const react_native_root_siblings_1 = __importDefault(require("react-native-root-
48
48
  const react_native_pager_view_1 = __importDefault(require("react-native-pager-view"));
49
49
  const react_native_webview_1 = require("react-native-webview");
50
50
  const RampKitNative_1 = require("./RampKitNative");
51
+ const OnboardingResponseStorage_1 = require("./OnboardingResponseStorage");
51
52
  // Reuse your injected script from App
52
53
  exports.injectedHardening = `
53
54
  (function(){
@@ -1652,7 +1653,7 @@ function Overlay(props) {
1652
1653
  sendOnboardingStateToWebView(i);
1653
1654
  }
1654
1655
  }, onMessage: (ev) => {
1655
- var _a, _b, _c, _d;
1656
+ var _a, _b, _c, _d, _e, _f;
1656
1657
  const raw = ev.nativeEvent.data;
1657
1658
  console.log("raw", raw);
1658
1659
  // Accept either raw strings or JSON payloads from your editor
@@ -1774,6 +1775,21 @@ function Overlay(props) {
1774
1775
  catch (_) { }
1775
1776
  return;
1776
1777
  }
1778
+ // 7) Question answered - persist response locally
1779
+ if ((data === null || data === void 0 ? void 0 : data.type) === "rampkit:question-answered") {
1780
+ const questionId = data === null || data === void 0 ? void 0 : data.questionId;
1781
+ if (questionId) {
1782
+ const response = {
1783
+ questionId,
1784
+ answer: (_c = data === null || data === void 0 ? void 0 : data.answer) !== null && _c !== void 0 ? _c : "",
1785
+ questionText: data === null || data === void 0 ? void 0 : data.questionText,
1786
+ screenName: (_d = props.screens[i]) === null || _d === void 0 ? void 0 : _d.id,
1787
+ answeredAt: new Date().toISOString(),
1788
+ };
1789
+ OnboardingResponseStorage_1.OnboardingResponseStorage.saveResponse(response);
1790
+ }
1791
+ return;
1792
+ }
1777
1793
  if ((data === null || data === void 0 ? void 0 : data.type) === "rampkit:continue" ||
1778
1794
  (data === null || data === void 0 ? void 0 : data.type) === "continue") {
1779
1795
  handleAdvance(i, (data === null || data === void 0 ? void 0 : data.animation) || "fade");
@@ -1811,7 +1827,7 @@ function Overlay(props) {
1811
1827
  return;
1812
1828
  }
1813
1829
  }
1814
- catch (_e) {
1830
+ catch (_g) {
1815
1831
  // String path
1816
1832
  if (raw === "rampkit:tap" ||
1817
1833
  raw === "next" ||
@@ -1838,7 +1854,7 @@ function Overlay(props) {
1838
1854
  if (raw === "rampkit:onboarding-finished") {
1839
1855
  setOnboardingCompleted(true);
1840
1856
  try {
1841
- (_c = props.onOnboardingFinished) === null || _c === void 0 ? void 0 : _c.call(props, undefined);
1857
+ (_e = props.onOnboardingFinished) === null || _e === void 0 ? void 0 : _e.call(props, undefined);
1842
1858
  }
1843
1859
  catch (_) { }
1844
1860
  handleRequestClose({ completed: true });
@@ -1846,7 +1862,7 @@ function Overlay(props) {
1846
1862
  }
1847
1863
  if (raw === "rampkit:show-paywall") {
1848
1864
  try {
1849
- (_d = props.onShowPaywall) === null || _d === void 0 ? void 0 : _d.call(props);
1865
+ (_f = props.onShowPaywall) === null || _f === void 0 ? void 0 : _f.call(props);
1850
1866
  }
1851
1867
  catch (_) { }
1852
1868
  return;
@@ -13,6 +13,7 @@ export declare const STORAGE_KEYS: {
13
13
  readonly INSTALL_DATE: "rk_install_date";
14
14
  readonly LAUNCH_COUNT: "rk_launch_count";
15
15
  readonly LAST_LAUNCH: "rk_last_launch";
16
+ readonly ONBOARDING_RESPONSES: "rk_onboarding_responses";
16
17
  };
17
18
  export declare const CAPABILITIES: readonly ["onboarding", "paywall_event_receiver", "haptic_feedback", "push_notifications"];
18
19
  export declare const SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InV1c3RsenV2am1vY2h4a3hhdGZ4Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MzU1NjQ0NjYsImV4cCI6MjA1MTE0MDQ2Nn0.5cNrph5LHmssNo39UKpULkC9n4OD5n6gsnTEQV-gwQk";
@@ -18,6 +18,8 @@ exports.STORAGE_KEYS = {
18
18
  INSTALL_DATE: "rk_install_date",
19
19
  LAUNCH_COUNT: "rk_launch_count",
20
20
  LAST_LAUNCH: "rk_last_launch",
21
+ // Onboarding responses (persisted)
22
+ ONBOARDING_RESPONSES: "rk_onboarding_responses",
21
23
  };
22
24
  exports.CAPABILITIES = [
23
25
  "onboarding",
package/build/index.d.ts CHANGED
@@ -11,5 +11,5 @@ export { default as RampKitNative } from "./RampKitNative";
11
11
  export type { NativeDeviceInfo, NativeLaunchData } from "./RampKitNative";
12
12
  export { Haptics, StoreReview, Notifications, TransactionObserver } from "./RampKitNative";
13
13
  export type { ImpactStyle, NotificationType, NotificationOptions, NotificationPermissionResult } from "./RampKitNative";
14
- export type { DeviceInfo, RampKitEvent, EventDevice, EventContext, RampKitConfig, RampKitEventName, RampKitContext, RampKitDeviceContext, RampKitUserContext, NavigationData, ScreenPosition, AppSessionStartedProperties, AppSessionEndedProperties, AppBackgroundedProperties, AppForegroundedProperties, OnboardingStartedProperties, OnboardingScreenViewedProperties, OnboardingQuestionAnsweredProperties, OnboardingCompletedProperties, OnboardingAbandonedProperties, ScreenViewProperties, CtaTapProperties, NotificationsPromptShownProperties, NotificationsResponseProperties, PaywallShownProperties, PaywallPrimaryActionTapProperties, PaywallClosedProperties, PurchaseStartedProperties, PurchaseCompletedProperties, PurchaseFailedProperties, } from "./types";
14
+ export type { DeviceInfo, RampKitEvent, EventDevice, EventContext, RampKitConfig, RampKitEventName, RampKitContext, RampKitDeviceContext, RampKitUserContext, NavigationData, ScreenPosition, OnboardingResponse, AppSessionStartedProperties, AppSessionEndedProperties, AppBackgroundedProperties, AppForegroundedProperties, OnboardingStartedProperties, OnboardingScreenViewedProperties, OnboardingQuestionAnsweredProperties, OnboardingCompletedProperties, OnboardingAbandonedProperties, ScreenViewProperties, CtaTapProperties, NotificationsPromptShownProperties, NotificationsResponseProperties, PaywallShownProperties, PaywallPrimaryActionTapProperties, PaywallClosedProperties, PurchaseStartedProperties, PurchaseCompletedProperties, PurchaseFailedProperties, } from "./types";
15
15
  export { SDK_VERSION, CAPABILITIES } from "./constants";
package/build/types.d.ts CHANGED
@@ -3,6 +3,12 @@
3
3
  */
4
4
  export interface DeviceInfo {
5
5
  appUserId: string;
6
+ /**
7
+ * Custom App User ID provided by the developer.
8
+ * This is an alias for their own user identification system.
9
+ * Does NOT replace appUserId - RampKit still uses its own generated ID.
10
+ */
11
+ appUserID: string | null;
6
12
  vendorId: string | null;
7
13
  appSessionId: string;
8
14
  installDate: string;
@@ -175,6 +181,16 @@ export interface RampKitConfig {
175
181
  onOnboardingFinished?: (payload?: any) => void;
176
182
  onShowPaywall?: (payload?: any) => void;
177
183
  showPaywall?: (payload?: any) => void;
184
+ /**
185
+ * Optional custom App User ID to associate with this user.
186
+ * This is an alias for your own user identification system - it does NOT replace
187
+ * the RampKit-generated user ID (appUserId). RampKit will continue to generate
188
+ * and use its own stable UUID for internal tracking.
189
+ *
190
+ * Use this to link RampKit analytics with your own user database.
191
+ * Can also be set later via RampKit.setAppUserID().
192
+ */
193
+ appUserID?: string;
178
194
  }
179
195
  /**
180
196
  * Device context variables available in templates as ${device.xxx}
@@ -250,3 +266,18 @@ export interface ScreenPosition {
250
266
  /** Row classification: "main" for main row screens, "variant" for screens below */
251
267
  row: "main" | "variant";
252
268
  }
269
+ /**
270
+ * Represents a single onboarding question response stored locally
271
+ */
272
+ export interface OnboardingResponse {
273
+ /** Unique identifier for the question */
274
+ questionId: string;
275
+ /** The user's answer (can be any JSON-serializable value) */
276
+ answer: any;
277
+ /** Optional text of the question shown to user */
278
+ questionText?: string;
279
+ /** Screen where the question was answered */
280
+ screenName?: string;
281
+ /** ISO 8601 timestamp when the answer was recorded */
282
+ answeredAt: string;
283
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rampkit-expo-dev",
3
- "version": "0.0.57",
3
+ "version": "0.0.60",
4
4
  "description": "The Expo SDK for RampKit. Build, test, and personalize app onboardings with instant updates.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",