@dashx/react-native 1.1.2 → 1.1.3

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.
@@ -0,0 +1,363 @@
1
+ const mockNativeModule = {
2
+ configure: jest.fn(),
3
+ identify: jest.fn(),
4
+ setIdentity: jest.fn(),
5
+ reset: jest.fn(),
6
+ track: jest.fn(),
7
+ screen: jest.fn(),
8
+ fetchRecord: jest.fn(),
9
+ searchRecords: jest.fn(),
10
+ fetchStoredPreferences: jest.fn(),
11
+ saveStoredPreferences: jest.fn(),
12
+ subscribe: jest.fn(),
13
+ unsubscribe: jest.fn(),
14
+ setLogLevel: jest.fn(),
15
+ uploadAsset: jest.fn(),
16
+ fetchAsset: jest.fn(),
17
+ enableLifecycleTracking: jest.fn(),
18
+ enableAdTracking: jest.fn(),
19
+ requestNotificationPermission: jest.fn(),
20
+ getNotificationPermissionStatus: jest.fn(),
21
+ addListener: jest.fn(),
22
+ removeListeners: jest.fn(),
23
+ };
24
+
25
+ const mockAddListener = jest.fn(() => ({ remove: jest.fn() }));
26
+
27
+ jest.mock('react-native', () => ({
28
+ NativeModules: {
29
+ DashXReactNative: mockNativeModule,
30
+ },
31
+ NativeEventEmitter: jest.fn().mockImplementation(() => ({
32
+ addListener: mockAddListener,
33
+ })),
34
+ Platform: {
35
+ select: jest.fn((obj) => obj.default || ''),
36
+ },
37
+ }));
38
+
39
+ const DashX = require('../index').default;
40
+
41
+ beforeEach(() => {
42
+ jest.clearAllMocks();
43
+ });
44
+
45
+ // --- configure ---
46
+
47
+ describe('configure', () => {
48
+ it('calls native configure with options', () => {
49
+ DashX.configure({ publicKey: 'pk_test' });
50
+ expect(mockNativeModule.configure).toHaveBeenCalledWith({
51
+ publicKey: 'pk_test',
52
+ });
53
+ });
54
+
55
+ it('throws if options is not an object', () => {
56
+ expect(() => DashX.configure(null)).toThrow('options must be an object');
57
+ expect(() => DashX.configure('string')).toThrow('options must be an object');
58
+ });
59
+
60
+ it('throws if publicKey is missing', () => {
61
+ expect(() => DashX.configure({})).toThrow('publicKey is required');
62
+ });
63
+
64
+ it('throws if publicKey is not a string', () => {
65
+ expect(() => DashX.configure({ publicKey: 123 })).toThrow(
66
+ 'publicKey is required and must be a string'
67
+ );
68
+ });
69
+ });
70
+
71
+ // --- identify ---
72
+
73
+ describe('identify', () => {
74
+ it('calls native identify with options', () => {
75
+ DashX.identify({ email: 'test@example.com' });
76
+ expect(mockNativeModule.identify).toHaveBeenCalledWith({
77
+ email: 'test@example.com',
78
+ });
79
+ });
80
+
81
+ it('throws if options is not an object', () => {
82
+ expect(() => DashX.identify(null)).toThrow('options must be an object');
83
+ });
84
+ });
85
+
86
+ // --- setIdentity ---
87
+
88
+ describe('setIdentity', () => {
89
+ it('calls native setIdentity with uid and token', () => {
90
+ DashX.setIdentity('user-1', 'token-abc');
91
+ expect(mockNativeModule.setIdentity).toHaveBeenCalledWith(
92
+ 'user-1',
93
+ 'token-abc'
94
+ );
95
+ });
96
+
97
+ it('throws if uid is missing', () => {
98
+ expect(() => DashX.setIdentity(null, 'token')).toThrow('uid is required');
99
+ });
100
+
101
+ it('throws if token is missing', () => {
102
+ expect(() => DashX.setIdentity('uid', null)).toThrow(
103
+ 'token is required'
104
+ );
105
+ });
106
+ });
107
+
108
+ // --- reset ---
109
+
110
+ describe('reset', () => {
111
+ it('calls native reset', () => {
112
+ DashX.reset();
113
+ expect(mockNativeModule.reset).toHaveBeenCalled();
114
+ });
115
+ });
116
+
117
+ // --- track ---
118
+
119
+ describe('track', () => {
120
+ it('calls native track with event and data', () => {
121
+ DashX.track('purchase', { amount: 100 });
122
+ expect(mockNativeModule.track).toHaveBeenCalledWith('purchase', {
123
+ amount: 100,
124
+ });
125
+ });
126
+
127
+ it('calls native track with null data when omitted', () => {
128
+ DashX.track('page_view');
129
+ expect(mockNativeModule.track).toHaveBeenCalledWith('page_view', null);
130
+ });
131
+
132
+ it('throws if event is missing', () => {
133
+ expect(() => DashX.track(null)).toThrow('event is required');
134
+ });
135
+
136
+ it('throws if event is not a string', () => {
137
+ expect(() => DashX.track(42)).toThrow(
138
+ 'event is required and must be a string'
139
+ );
140
+ });
141
+ });
142
+
143
+ // --- screen ---
144
+
145
+ describe('screen', () => {
146
+ it('calls native screen with name and data', () => {
147
+ DashX.screen('Home', { section: 'top' });
148
+ expect(mockNativeModule.screen).toHaveBeenCalledWith('Home', {
149
+ section: 'top',
150
+ });
151
+ });
152
+
153
+ it('calls native screen with null data when omitted', () => {
154
+ DashX.screen('Settings');
155
+ expect(mockNativeModule.screen).toHaveBeenCalledWith('Settings', null);
156
+ });
157
+
158
+ it('throws if screenName is missing', () => {
159
+ expect(() => DashX.screen(null)).toThrow('screenName is required');
160
+ });
161
+ });
162
+
163
+ // --- fetchRecord ---
164
+
165
+ describe('fetchRecord', () => {
166
+ it('calls native fetchRecord with urn and options', () => {
167
+ mockNativeModule.fetchRecord.mockResolvedValue({ id: '123' });
168
+ const result = DashX.fetchRecord('article/123', { preview: true });
169
+ expect(mockNativeModule.fetchRecord).toHaveBeenCalledWith('article/123', {
170
+ preview: true,
171
+ });
172
+ return expect(result).resolves.toEqual({ id: '123' });
173
+ });
174
+
175
+ it('calls native fetchRecord with empty options when omitted', () => {
176
+ mockNativeModule.fetchRecord.mockResolvedValue({});
177
+ DashX.fetchRecord('article/456');
178
+ expect(mockNativeModule.fetchRecord).toHaveBeenCalledWith(
179
+ 'article/456',
180
+ {}
181
+ );
182
+ });
183
+
184
+ it('throws if urn is missing', () => {
185
+ expect(() => DashX.fetchRecord(null)).toThrow('urn is required');
186
+ });
187
+ });
188
+
189
+ // --- searchRecords ---
190
+
191
+ describe('searchRecords', () => {
192
+ it('calls native searchRecords with resource and options', () => {
193
+ mockNativeModule.searchRecords.mockResolvedValue([{ id: '1' }]);
194
+ const result = DashX.searchRecords('articles', { limit: 10 });
195
+ expect(mockNativeModule.searchRecords).toHaveBeenCalledWith('articles', {
196
+ limit: 10,
197
+ });
198
+ return expect(result).resolves.toEqual([{ id: '1' }]);
199
+ });
200
+
201
+ it('throws if resource is missing', () => {
202
+ expect(() => DashX.searchRecords(null)).toThrow('resource is required');
203
+ });
204
+ });
205
+
206
+ // --- fetchStoredPreferences ---
207
+
208
+ describe('fetchStoredPreferences', () => {
209
+ it('calls native fetchStoredPreferences', () => {
210
+ mockNativeModule.fetchStoredPreferences.mockResolvedValue({
211
+ email: true,
212
+ });
213
+ const result = DashX.fetchStoredPreferences();
214
+ expect(mockNativeModule.fetchStoredPreferences).toHaveBeenCalled();
215
+ return expect(result).resolves.toEqual({ email: true });
216
+ });
217
+ });
218
+
219
+ // --- saveStoredPreferences ---
220
+
221
+ describe('saveStoredPreferences', () => {
222
+ it('calls native saveStoredPreferences with data', () => {
223
+ mockNativeModule.saveStoredPreferences.mockResolvedValue({ success: true });
224
+ const result = DashX.saveStoredPreferences({ email: false });
225
+ expect(mockNativeModule.saveStoredPreferences).toHaveBeenCalledWith({
226
+ email: false,
227
+ });
228
+ return expect(result).resolves.toEqual({ success: true });
229
+ });
230
+
231
+ it('throws if preferenceData is not an object', () => {
232
+ expect(() => DashX.saveStoredPreferences(null)).toThrow(
233
+ 'preferenceData is required'
234
+ );
235
+ });
236
+ });
237
+
238
+ // --- subscribe / unsubscribe ---
239
+
240
+ describe('subscribe', () => {
241
+ it('calls native subscribe', () => {
242
+ DashX.subscribe();
243
+ expect(mockNativeModule.subscribe).toHaveBeenCalled();
244
+ });
245
+ });
246
+
247
+ describe('unsubscribe', () => {
248
+ it('calls native unsubscribe', () => {
249
+ DashX.unsubscribe();
250
+ expect(mockNativeModule.unsubscribe).toHaveBeenCalled();
251
+ });
252
+ });
253
+
254
+ // --- setLogLevel ---
255
+
256
+ describe('setLogLevel', () => {
257
+ it('calls native setLogLevel with level', () => {
258
+ DashX.setLogLevel(2);
259
+ expect(mockNativeModule.setLogLevel).toHaveBeenCalledWith(2);
260
+ });
261
+
262
+ it('throws if level is not a number', () => {
263
+ expect(() => DashX.setLogLevel('debug')).toThrow(
264
+ 'level must be a number'
265
+ );
266
+ });
267
+ });
268
+
269
+ // --- onMessageReceived ---
270
+
271
+ describe('onMessageReceived', () => {
272
+ it('throws if callback is not a function', () => {
273
+ expect(() => DashX.onMessageReceived('not a function')).toThrow(
274
+ 'callback must be a function'
275
+ );
276
+ });
277
+
278
+ it('registers a listener for messageReceived events', () => {
279
+ const callback = jest.fn();
280
+ const subscription = DashX.onMessageReceived(callback);
281
+ expect(mockAddListener).toHaveBeenCalledWith('messageReceived', callback);
282
+ expect(subscription).toBeDefined();
283
+ });
284
+ });
285
+
286
+ // --- uploadAsset ---
287
+
288
+ describe('uploadAsset', () => {
289
+ it('calls native uploadAsset with filePath, resource, attribute', () => {
290
+ mockNativeModule.uploadAsset.mockResolvedValue({ status: 'ready' });
291
+ const result = DashX.uploadAsset('/path/to/file.jpg', 'products', 'image');
292
+ expect(mockNativeModule.uploadAsset).toHaveBeenCalledWith(
293
+ '/path/to/file.jpg',
294
+ 'products',
295
+ 'image'
296
+ );
297
+ return expect(result).resolves.toEqual({ status: 'ready' });
298
+ });
299
+
300
+ it('throws if filePath is missing', () => {
301
+ expect(() => DashX.uploadAsset(null, 'r', 'a')).toThrow('filePath must be a string');
302
+ });
303
+
304
+ it('throws if resource is missing', () => {
305
+ expect(() => DashX.uploadAsset('/path', null, 'a')).toThrow('resource must be a string');
306
+ });
307
+
308
+ it('throws if attribute is missing', () => {
309
+ expect(() => DashX.uploadAsset('/path', 'r', null)).toThrow('attribute must be a string');
310
+ });
311
+ });
312
+
313
+ // --- fetchAsset ---
314
+
315
+ describe('fetchAsset', () => {
316
+ it('calls native fetchAsset with assetId', () => {
317
+ mockNativeModule.fetchAsset.mockResolvedValue({ status: 'ready' });
318
+ const result = DashX.fetchAsset('asset-123');
319
+ expect(mockNativeModule.fetchAsset).toHaveBeenCalledWith('asset-123');
320
+ return expect(result).resolves.toEqual({ status: 'ready' });
321
+ });
322
+
323
+ it('throws if assetId is missing', () => {
324
+ expect(() => DashX.fetchAsset(null)).toThrow('assetId must be a string');
325
+ });
326
+ });
327
+
328
+ // --- requestNotificationPermission / getNotificationPermissionStatus ---
329
+
330
+ describe('requestNotificationPermission', () => {
331
+ it('calls native requestNotificationPermission', () => {
332
+ mockNativeModule.requestNotificationPermission.mockResolvedValue(2);
333
+ const result = DashX.requestNotificationPermission();
334
+ expect(mockNativeModule.requestNotificationPermission).toHaveBeenCalled();
335
+ return expect(result).resolves.toBe(2);
336
+ });
337
+ });
338
+
339
+ describe('getNotificationPermissionStatus', () => {
340
+ it('calls native getNotificationPermissionStatus', () => {
341
+ mockNativeModule.getNotificationPermissionStatus.mockResolvedValue(1);
342
+ const result = DashX.getNotificationPermissionStatus();
343
+ expect(mockNativeModule.getNotificationPermissionStatus).toHaveBeenCalled();
344
+ return expect(result).resolves.toBe(1);
345
+ });
346
+ });
347
+
348
+ // --- onLinkReceived ---
349
+
350
+ describe('onLinkReceived', () => {
351
+ it('throws if callback is not a function', () => {
352
+ expect(() => DashX.onLinkReceived('not a function')).toThrow(
353
+ 'callback must be a function'
354
+ );
355
+ });
356
+
357
+ it('registers a listener for linkReceived events', () => {
358
+ const callback = jest.fn();
359
+ const subscription = DashX.onLinkReceived(callback);
360
+ expect(mockAddListener).toHaveBeenCalledWith('linkReceived', callback);
361
+ expect(subscription).toBeDefined();
362
+ });
363
+ });
File without changes
@@ -0,0 +1,2 @@
1
+ #Mon Mar 23 22:47:55 IST 2026
2
+ gradle.version=7.2
File without changes
@@ -55,5 +55,5 @@ dependencies {
55
55
  // Ensure Firebase Messaging types are available for FCM service subclass
56
56
  implementation 'com.google.firebase:firebase-messaging-ktx:23.1.2'
57
57
 
58
- implementation "com.dashx:dashx-android:1.1.4"
58
+ implementation "com.dashx:dashx-android:1.1.5"
59
59
  }
package/babel.config.js CHANGED
@@ -1,3 +1,3 @@
1
1
  module.exports = {
2
- presets: ['module:metro-react-native-babel-preset'],
2
+ presets: ['module:@react-native/babel-preset'],
3
3
  };
package/index.d.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  import { EmitterSubscription } from 'react-native';
2
2
 
3
+ /**
4
+ * Error codes returned by native SDKs in promise rejections.
5
+ * Consistent across iOS and Android.
6
+ */
7
+ export enum DashXErrorCode {
8
+ /** An unspecified error occurred. */
9
+ EUNSPECIFIED = 'EUNSPECIFIED',
10
+ }
11
+
3
12
  export interface ConfigureOptions {
4
13
  publicKey: string;
5
14
  baseURI?: string;
@@ -28,6 +37,7 @@ export interface SearchRecordsOptions {
28
37
  filter?: Record<string, unknown>;
29
38
  order?: Record<string, unknown>[];
30
39
  limit?: number;
40
+ page?: number;
31
41
  preview?: boolean;
32
42
  language?: string;
33
43
  fields?: Record<string, unknown>[];
@@ -35,6 +45,23 @@ export interface SearchRecordsOptions {
35
45
  exclude?: Record<string, unknown>[];
36
46
  }
37
47
 
48
+ export interface AssetData {
49
+ status?: string;
50
+ url?: string;
51
+ playbackIds?: { id?: string; policy?: string }[];
52
+ }
53
+
54
+ export interface AssetResponse {
55
+ status?: string;
56
+ data?: { asset?: AssetData };
57
+ }
58
+
59
+ /**
60
+ * Mirrors UNAuthorizationStatus integer values:
61
+ * 0 = notDetermined, 1 = denied, 2 = authorized, 3 = provisional, 4 = ephemeral
62
+ */
63
+ export type NotificationPermissionStatus = 0 | 1 | 2 | 3 | 4;
64
+
38
65
  export interface NotificationMessage {
39
66
  [key: string]: unknown;
40
67
  }
@@ -123,6 +150,49 @@ export interface DashXInstance {
123
150
  onMessageReceived(
124
151
  callback: (message: NotificationMessage) => void
125
152
  ): EmitterSubscription;
153
+
154
+ /**
155
+ * Upload a local file to DashX. Returns an AssetResponse with the asset status and URL.
156
+ * @param filePath Absolute path to the local file.
157
+ */
158
+ uploadAsset(
159
+ filePath: string,
160
+ resource: string,
161
+ attribute: string
162
+ ): Promise<AssetResponse>;
163
+
164
+ /**
165
+ * Fetch details of a previously uploaded asset by its ID.
166
+ */
167
+ fetchAsset(assetId: string): Promise<AssetResponse>;
168
+
169
+ /**
170
+ * Enable automatic app lifecycle event tracking (iOS only).
171
+ */
172
+ enableLifecycleTracking(): void;
173
+
174
+ /**
175
+ * Enable IDFA/ATT ad tracking and request the App Tracking Transparency permission (iOS only).
176
+ */
177
+ enableAdTracking(): void;
178
+
179
+ /**
180
+ * Request push notification permission from the user.
181
+ * Resolves with a NotificationPermissionStatus integer.
182
+ */
183
+ requestNotificationPermission(): Promise<NotificationPermissionStatus>;
184
+
185
+ /**
186
+ * Get the current push notification permission status without prompting.
187
+ * Resolves with a NotificationPermissionStatus integer.
188
+ */
189
+ getNotificationPermissionStatus(): Promise<NotificationPermissionStatus>;
190
+
191
+ /**
192
+ * Register a listener for deep link / universal link events dispatched by the SDK.
193
+ * Returns a subscription that can be removed via `.remove()`.
194
+ */
195
+ onLinkReceived(callback: (url: string) => void): EmitterSubscription;
126
196
  }
127
197
 
128
198
  declare const DashX: DashXInstance;
package/index.js CHANGED
@@ -40,7 +40,9 @@ const DashX = {
40
40
  throw new Error('DashX.configure: options must be an object');
41
41
  }
42
42
  if (!options.publicKey || typeof options.publicKey !== 'string') {
43
- throw new Error('DashX.configure: publicKey is required and must be a string');
43
+ throw new Error(
44
+ 'DashX.configure: publicKey is required and must be a string'
45
+ );
44
46
  }
45
47
  return getNativeDashX().configure(options);
46
48
  },
@@ -54,10 +56,14 @@ const DashX = {
54
56
 
55
57
  setIdentity(uid, token) {
56
58
  if (!uid || typeof uid !== 'string') {
57
- throw new Error('DashX.setIdentity: uid is required and must be a string');
59
+ throw new Error(
60
+ 'DashX.setIdentity: uid is required and must be a string'
61
+ );
58
62
  }
59
63
  if (!token || typeof token !== 'string') {
60
- throw new Error('DashX.setIdentity: token is required and must be a string');
64
+ throw new Error(
65
+ 'DashX.setIdentity: token is required and must be a string'
66
+ );
61
67
  }
62
68
  return getNativeDashX().setIdentity(uid, token);
63
69
  },
@@ -75,14 +81,18 @@ const DashX = {
75
81
 
76
82
  screen(screenName, data) {
77
83
  if (!screenName || typeof screenName !== 'string') {
78
- throw new Error('DashX.screen: screenName is required and must be a string');
84
+ throw new Error(
85
+ 'DashX.screen: screenName is required and must be a string'
86
+ );
79
87
  }
80
88
  return getNativeDashX().screen(screenName, data || null);
81
89
  },
82
90
 
83
91
  fetchRecord(urn, options = {}) {
84
92
  if (!urn || typeof urn !== 'string') {
85
- throw new Error('DashX.fetchRecord: urn is required and must be a string (e.g. "article/123")');
93
+ throw new Error(
94
+ 'DashX.fetchRecord: urn is required and must be a string (e.g. "article/123")'
95
+ );
86
96
  }
87
97
  if (options !== null && typeof options !== 'object') {
88
98
  throw new Error('DashX.fetchRecord: options must be an object');
@@ -92,7 +102,9 @@ const DashX = {
92
102
 
93
103
  searchRecords(resource, options = {}) {
94
104
  if (!resource || typeof resource !== 'string') {
95
- throw new Error('DashX.searchRecords: resource is required and must be a string');
105
+ throw new Error(
106
+ 'DashX.searchRecords: resource is required and must be a string'
107
+ );
96
108
  }
97
109
  if (options !== null && typeof options !== 'object') {
98
110
  throw new Error('DashX.searchRecords: options must be an object');
@@ -134,6 +146,51 @@ const DashX = {
134
146
  }
135
147
  return getEventEmitter().addListener('messageReceived', callback);
136
148
  },
149
+
150
+ uploadAsset(filePath, resource, attribute) {
151
+ if (!filePath || typeof filePath !== 'string') {
152
+ throw new Error('DashX.uploadAsset: filePath must be a string');
153
+ }
154
+ if (!resource || typeof resource !== 'string') {
155
+ throw new Error('DashX.uploadAsset: resource must be a string');
156
+ }
157
+ if (!attribute || typeof attribute !== 'string') {
158
+ throw new Error('DashX.uploadAsset: attribute must be a string');
159
+ }
160
+ return getNativeDashX().uploadAsset(filePath, resource, attribute);
161
+ },
162
+
163
+ fetchAsset(assetId) {
164
+ if (!assetId || typeof assetId !== 'string') {
165
+ throw new Error('DashX.fetchAsset: assetId must be a string');
166
+ }
167
+ return getNativeDashX().fetchAsset(assetId);
168
+ },
169
+
170
+ enableLifecycleTracking() {
171
+ if (Platform.OS !== 'ios') return;
172
+ return getNativeDashX().enableLifecycleTracking();
173
+ },
174
+
175
+ enableAdTracking() {
176
+ if (Platform.OS !== 'ios') return;
177
+ return getNativeDashX().enableAdTracking();
178
+ },
179
+
180
+ requestNotificationPermission() {
181
+ return getNativeDashX().requestNotificationPermission();
182
+ },
183
+
184
+ getNotificationPermissionStatus() {
185
+ return getNativeDashX().getNotificationPermissionStatus();
186
+ },
187
+
188
+ onLinkReceived(callback) {
189
+ if (typeof callback !== 'function') {
190
+ throw new Error('DashX.onLinkReceived: callback must be a function');
191
+ }
192
+ return getEventEmitter().addListener('linkReceived', callback);
193
+ },
137
194
  };
138
195
 
139
196
  export default DashX;
package/ios/.DS_Store ADDED
Binary file