@amplytools/react-native-amply-sdk 0.11.0 → 0.12.1

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.
@@ -22,7 +22,7 @@ Pod::Spec.new do |s|
22
22
  s.dependency 'React-RCTFabric'
23
23
 
24
24
  # Amply KMP SDK from CocoaPods
25
- s.dependency 'AmplySDK', '~> 0.11.0'
25
+ s.dependency 'AmplySDK', '~> 0.12.0'
26
26
 
27
27
  s.libraries = 'c++'
28
28
  s.pod_target_xcconfig = {
package/README.md CHANGED
@@ -42,10 +42,12 @@ If you upgraded from v0.2.11 or earlier and have `"@amplytools/react-native-ampl
42
42
  ```typescript
43
43
  import Amply from '@amplytools/react-native-amply-sdk';
44
44
 
45
- // Initialize
45
+ // Initialize — all three keys are required (your application's page in Amply).
46
+ // Use the iOS application's keys on iOS and the Android application's on Android.
46
47
  await Amply.initialize({
47
48
  appId: 'YOUR_APP_ID',
48
49
  apiKeyPublic: 'YOUR_PUBLIC_API_KEY',
50
+ apiKeySecret: 'YOUR_SECRET_API_KEY',
49
51
  debug: true, // optional: enable debug logging
50
52
  });
51
53
 
@@ -67,6 +69,13 @@ const unsubscribe = await Amply.addDeepLinkListener((event) => {
67
69
  });
68
70
  ```
69
71
 
72
+ ### About the keys
73
+
74
+ `apiKeySecret` ships inside your app: the SDK signs every request with it on the device. Keep it
75
+ out of source control and inject it at build time, like the public key. If any of the three keys
76
+ is missing or blank, `initialize` rejects with `AMP_INVALID_CONFIG`, logs which key is missing,
77
+ and Amply stays off — every later call is a no-op. It never crashes your app.
78
+
70
79
  ## Documentation
71
80
 
72
81
  Full integration guides, API reference, and examples:
@@ -15,7 +15,7 @@ buildscript {
15
15
  apply plugin: 'com.android.library'
16
16
  apply plugin: 'org.jetbrains.kotlin.android'
17
17
 
18
- def amplySdkVersion = project.findProperty('amplySdkVersion') ?: System.getenv('AMPLY_SDK_VERSION') ?: '0.11.0'
18
+ def amplySdkVersion = project.findProperty('amplySdkVersion') ?: System.getenv('AMPLY_SDK_VERSION') ?: '0.12.0'
19
19
  group = 'tools.amply'
20
20
  version = amplySdkVersion
21
21
 
@@ -334,11 +334,20 @@ class AmplyModule(reactContext: ReactApplicationContext) :
334
334
 
335
335
  // ---- твои helper-методы без изменений ----
336
336
 
337
+ private fun ReadableMap.requiredString(key: String): String {
338
+ val value = if (hasKey(key)) getString(key) else null
339
+ if (value.isNullOrBlank()) throw IllegalArgumentException("'$key' is required")
340
+ return value
341
+ }
342
+
337
343
  private fun ReadableMap.toInitializationOptions(): AmplyInitializationOptions {
338
- val appId = getString("appId") ?: throw IllegalArgumentException("'appId' is required")
339
- val apiKeyPublic = getString("apiKeyPublic") ?: throw IllegalArgumentException("'apiKeyPublic' is required")
344
+ // All three keys are required, blank included: a blank secret used to pass straight through
345
+ // and sign every request with an empty key, which the backend refuses — a silent failure
346
+ // instead of this rejection.
347
+ val appId = requiredString("appId")
348
+ val apiKeyPublic = requiredString("apiKeyPublic")
349
+ val apiKeySecret = requiredString("apiKeySecret")
340
350
 
341
- val apiKeySecret = if (hasKey("apiKeySecret")) getString("apiKeySecret") else null
342
351
  val configBaseUrl = if (hasKey("configBaseUrl")) getString("configBaseUrl") else null
343
352
  val backendBaseUrl = if (hasKey("backendBaseUrl")) getString("backendBaseUrl") else null
344
353
  val endpoint = if (hasKey("endpoint")) getString("endpoint") else null
@@ -144,9 +144,16 @@ class DefaultAmplyClient(
144
144
  synchronized(propertyLock) {
145
145
  amplyInstance = instance
146
146
  if (pendingPropertyOps.isNotEmpty()) {
147
- android.util.Log.i("AmplyReactNative", "Draining ${pendingPropertyOps.size} buffered property operations")
148
- pendingPropertyOps.forEach { op -> op(instance) }
147
+ // Taken out of the list before running, and each op guarded: one throwing op used to
148
+ // abort the drain after amplyInstance was already set, so the rest were never
149
+ // replayed and initialize rejected while isInitialized() reported true.
150
+ val ops = pendingPropertyOps.toList()
149
151
  pendingPropertyOps.clear()
152
+ android.util.Log.i("AmplyReactNative", "Draining ${ops.size} buffered property operations")
153
+ ops.forEach { op ->
154
+ runCatching { op(instance) }
155
+ .onFailure { android.util.Log.w("AmplyReactNative", "Buffered property operation failed", it) }
156
+ }
150
157
  }
151
158
  }
152
159
 
@@ -362,9 +369,20 @@ class DefaultAmplyClient(
362
369
  }
363
370
 
364
371
  override fun setUserId(userId: String?) {
365
- val instance = requireInstance()
366
- instance.setUserId(userId)
367
- android.util.Log.i("AmplyReactNative", "User ID set to: ${userId ?: "<null>"}")
372
+ // Buffered like the property calls below. It used to requireInstance(), and this runs from a
373
+ // void TurboModule method, so the IllegalStateException never reached JS: calling setUserId
374
+ // before a successful initialize (for example after initialize rejected over a missing key)
375
+ // crashed the host app.
376
+ synchronized(propertyLock) {
377
+ val instance = amplyInstance
378
+ if (instance != null) {
379
+ instance.setUserId(userId)
380
+ android.util.Log.i("AmplyReactNative", "User ID set to: ${userId ?: "<null>"}")
381
+ } else {
382
+ android.util.Log.i("AmplyReactNative", "Buffering setUserId until init")
383
+ pendingPropertyOps.add { it.setUserId(userId) }
384
+ }
385
+ }
368
386
  }
369
387
 
370
388
  override fun setCustomProperty(key: String, value: Any) {
@@ -170,7 +170,7 @@ describe('gate registration lifecycle', () => {
170
170
  // Native refuses while uninitialised; JS must not record that as done.
171
171
  expect(mockNativeModule.registerGate).not.toHaveBeenCalled();
172
172
  mockNativeModule.isInitialized.mockReturnValue(true);
173
- await Amply.initialize({ apiKey: 'k', apiKeySecret: 's' });
173
+ await Amply.initialize({ appId: 'a', apiKeyPublic: 'k', apiKeySecret: 's' });
174
174
  expect(mockNativeModule.registerGate).toHaveBeenCalledWith('stillframe://ad', 'cancel', 0);
175
175
  emitPresentation();
176
176
  await flushMicrotasks();
@@ -181,7 +181,7 @@ describe('gate registration lifecycle', () => {
181
181
  await Amply.addDeepLinkListener(jest.fn());
182
182
  expect(mockNativeModule.registerDeepLinkListener).not.toHaveBeenCalled();
183
183
  mockNativeModule.isInitialized.mockReturnValue(true);
184
- await Amply.initialize({ apiKey: 'k', apiKeySecret: 's' });
184
+ await Amply.initialize({ appId: 'a', apiKeyPublic: 'k', apiKeySecret: 's' });
185
185
  expect(mockNativeModule.registerDeepLinkListener).toHaveBeenCalledTimes(1);
186
186
  });
187
187
  /**
@@ -197,7 +197,7 @@ describe('gate registration lifecycle', () => {
197
197
  await Amply.registerGate('stillframe://ad', first);
198
198
  await Amply.registerGate('stillframe://ad', second);
199
199
  mockNativeModule.isInitialized.mockReturnValue(true);
200
- await Amply.initialize({ apiKey: 'k', apiKeySecret: 's' });
200
+ await Amply.initialize({ appId: 'a', apiKeyPublic: 'k', apiKeySecret: 's' });
201
201
  emitPresentation();
202
202
  await flushMicrotasks();
203
203
  expect(second).toHaveBeenCalledTimes(1);
@@ -208,7 +208,7 @@ describe('gate registration lifecycle', () => {
208
208
  const unsubscribe = await Amply.registerGate('stillframe://ad', jest.fn());
209
209
  unsubscribe();
210
210
  mockNativeModule.isInitialized.mockReturnValue(true);
211
- await Amply.initialize({ apiKey: 'k', apiKeySecret: 's' });
211
+ await Amply.initialize({ appId: 'a', apiKeyPublic: 'k', apiKeySecret: 's' });
212
212
  expect(mockNativeModule.registerGate).not.toHaveBeenCalled();
213
213
  });
214
214
  });
@@ -25,13 +25,36 @@ describe('Amply JS API', () => {
25
25
  __setNativeModule(mockNativeModule);
26
26
  });
27
27
  it('initializes the native module', async () => {
28
- await Amply.initialize({ appId: 'id', apiKeyPublic: 'public' });
28
+ await Amply.initialize({ appId: 'id', apiKeyPublic: 'public', apiKeySecret: 'secret' });
29
29
  expect(mockNativeModule.initialize).toHaveBeenCalledWith(expect.objectContaining({ appId: 'id', apiKeyPublic: 'public' }));
30
30
  });
31
+ // A missing key used to reach the native SDK, whose config builder threw an exception that
32
+ // crossed into Objective-C uncaught and aborted the host app — no JS try/catch could stop it.
33
+ // Bad keys must cost the app its Amply, never its process.
34
+ describe.each([
35
+ ['apiKeySecret', { appId: 'id', apiKeyPublic: 'public' }],
36
+ ['apiKeySecret', { appId: 'id', apiKeyPublic: 'public', apiKeySecret: '' }],
37
+ ['apiKeySecret', { appId: 'id', apiKeyPublic: 'public', apiKeySecret: ' ' }],
38
+ ['apiKeySecret', { appId: 'id', apiKeyPublic: 'public', apiKeySecret: null }],
39
+ ['apiKeyPublic', { appId: 'id', apiKeyPublic: '', apiKeySecret: 'secret' }],
40
+ ['appId', { appId: '', apiKeyPublic: 'public', apiKeySecret: 'secret' }],
41
+ ])('when %s is missing (%j)', (field, config) => {
42
+ it('rejects with AMP_INVALID_CONFIG, names the field and never calls the native module', async () => {
43
+ const error = jest.spyOn(console, 'error').mockImplementation(() => { });
44
+ await expect(Amply.initialize(config)).rejects.toMatchObject({
45
+ code: 'AMP_INVALID_CONFIG',
46
+ message: expect.stringContaining(field),
47
+ });
48
+ expect(mockNativeModule.initialize).not.toHaveBeenCalled();
49
+ expect(error).toHaveBeenCalledWith(expect.stringContaining(field));
50
+ error.mockRestore();
51
+ });
52
+ });
31
53
  it('passes the base-URL overrides through to the native layer', async () => {
32
54
  await Amply.initialize({
33
55
  appId: 'id',
34
56
  apiKeyPublic: 'public',
57
+ apiKeySecret: 'secret',
35
58
  configBaseUrl: 'https://config-staging.example.com',
36
59
  backendBaseUrl: 'https://api-staging.example.com',
37
60
  });
@@ -47,6 +70,7 @@ describe('Amply JS API', () => {
47
70
  await Amply.initialize({
48
71
  appId: 'id',
49
72
  apiKeyPublic: 'public',
73
+ apiKeySecret: 'secret',
50
74
  endpoint: 'https://api-staging.example.com',
51
75
  });
52
76
  expect(mockNativeModule.initialize).toHaveBeenCalledWith(expect.objectContaining({ endpoint: 'https://api-staging.example.com' }));
@@ -1,7 +1,21 @@
1
1
  export { useAmplySystemEvents } from './hooks/useAmplySystemEvents';
2
2
  export { formatSystemEventLabel } from './systemEventUtils';
3
3
  export type { FormatOptions } from './systemEventUtils';
4
- import type { CampaignResult, GateDecision, AmplyInitializationConfig, DataSetSnapshot, DataSetType, DeepLinkEvent, EventRecord, LogLevel, CampaignPresentEvent, TrackEventPayload } from './nativeSpecs/NativeAmplyModule';
4
+ import type { CampaignResult, GateDecision, AmplyInitializationConfig as NativeInitializationConfig, DataSetSnapshot, DataSetType, DeepLinkEvent, EventRecord, LogLevel, CampaignPresentEvent, TrackEventPayload } from './nativeSpecs/NativeAmplyModule';
5
+ /**
6
+ * The keys from your application's page in Amply. All three are required: the SDK signs every
7
+ * request with `apiKeySecret` on the device, so the secret ships inside the app next to the
8
+ * public key. The codegen spec keeps the field optional only so the generated native structs
9
+ * stay unchanged; this is the contract callers see.
10
+ */
11
+ export type AmplyInitializationConfig = Omit<NativeInitializationConfig, 'apiKeySecret'> & {
12
+ apiKeySecret: string;
13
+ };
14
+ /** Rejection raised by {@link initialize} for a config the SDK cannot start with. */
15
+ export declare class AmplyConfigError extends Error {
16
+ readonly code = "AMP_INVALID_CONFIG";
17
+ constructor(message: string);
18
+ }
5
19
  export declare function initialize(config: AmplyInitializationConfig): Promise<void>;
6
20
  /**
7
21
  * Set the user ID for analytics. Pass null to clear.
@@ -151,7 +165,7 @@ export declare function removeCustomProperty(key: string): void;
151
165
  */
152
166
  export declare function clearCustomProperties(): void;
153
167
  export declare function removeAllListeners(): void;
154
- export type { AmplyInitializationConfig, DataSetSnapshot, DataSetType, DeepLinkEvent, EventRecord, LogLevel, CampaignPresentEvent, TrackEventPayload, };
168
+ export type { DataSetSnapshot, DataSetType, DeepLinkEvent, EventRecord, LogLevel, CampaignPresentEvent, TrackEventPayload, };
155
169
  export declare function addSystemEventsListener(listener: (event: EventRecord) => void): Promise<() => void>;
156
170
  export declare const systemEvents: {
157
171
  addListener: typeof addSystemEventsListener;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
package/dist/src/index.js CHANGED
@@ -2,6 +2,22 @@ import getNativeModule from './nativeModule';
2
2
  import { addSystemEventListenerRaw, addSystemEventListener as addSystemEventListenerInternal } from './systemEvents';
3
3
  export { useAmplySystemEvents } from './hooks/useAmplySystemEvents';
4
4
  export { formatSystemEventLabel } from './systemEventUtils';
5
+ const REQUIRED_KEYS = ['appId', 'apiKeyPublic', 'apiKeySecret'];
6
+ /** Rejection raised by {@link initialize} for a config the SDK cannot start with. */
7
+ export class AmplyConfigError extends Error {
8
+ constructor(message) {
9
+ super(message);
10
+ this.code = 'AMP_INVALID_CONFIG';
11
+ this.name = 'AmplyConfigError';
12
+ }
13
+ }
14
+ /** The first required key that is missing or blank, if any. */
15
+ function missingKey(config) {
16
+ return REQUIRED_KEYS.find((key) => {
17
+ const value = config === null || config === void 0 ? void 0 : config[key];
18
+ return typeof value !== 'string' || value.trim().length === 0;
19
+ });
20
+ }
5
21
  let deepLinkRegistered = false;
6
22
  let debugLogListenerRegistered = false;
7
23
  const deepLinkSubscriptions = new Set();
@@ -157,6 +173,15 @@ function trackDeepLinkSubscription(subscription) {
157
173
  return unsubscribe;
158
174
  }
159
175
  export async function initialize(config) {
176
+ // Checked here, before the native module sees anything: a missing key used to reach the
177
+ // native config builder, which threw across the Objective-C boundary and aborted the host
178
+ // app. Bad keys cost the app its Amply — never its process. The SDK stays uninitialized.
179
+ const missing = missingKey(config);
180
+ if (missing) {
181
+ const message = `[Amply] '${missing}' is required — Amply is not initialized. Copy it from your application's page in Amply.`;
182
+ console.error(message);
183
+ throw new AmplyConfigError(message);
184
+ }
160
185
  // Set up debug log listener if debug mode is enabled
161
186
  if (config.debug || config.logLevel) {
162
187
  ensureDebugLogListener();