react-native-nami-sdk 3.4.0-dev.202605201800 → 3.4.0-dev.202605241634

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.
@@ -85,8 +85,8 @@ dependencies {
85
85
  implementation fileTree(dir: 'libs', include: ['*.jar'])
86
86
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
87
87
 
88
- playImplementation "com.namiml:sdk-android:3.4.0-dev.202605201800"
89
- amazonImplementation "com.namiml:sdk-amazon:3.4.0-dev.202605201800"
88
+ playImplementation "com.namiml:sdk-android:3.4.0-dev.202605241634"
89
+ amazonImplementation "com.namiml:sdk-amazon:3.4.0-dev.202605241634"
90
90
 
91
91
  implementation "com.facebook.react:react-native:+" // From node_modules
92
92
  coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:2.0.4"
@@ -1,3 +1,4 @@
1
+ import React from 'react';
1
2
  import { NativeEventEmitter } from 'react-native';
2
3
  export declare const NamiOverlayControl: {
3
4
  emitter: NativeEventEmitter;
@@ -5,5 +6,6 @@ export declare const NamiOverlayControl: {
5
6
  finishOverlay(result?: any): Promise<void>;
6
7
  onOverlayReady(handler: () => void): () => void;
7
8
  onOverlayResult(handler: (result: any) => void): () => void;
9
+ setOverlayContent(component: React.ComponentType | null): void;
8
10
  };
9
11
  export default function NamiOverlayHost(): any;
@@ -2,4 +2,4 @@
2
2
  * Auto-generated file. Do not edit manually.
3
3
  * React Native Nami SDK version.
4
4
  */
5
- export declare const NAMI_REACT_NATIVE_VERSION = "3.4.0-dev.202605201800";
5
+ export declare const NAMI_REACT_NATIVE_VERSION = "3.4.0-dev.202605241634";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nami-sdk",
3
- "version": "3.4.0-dev.202605201800",
3
+ "version": "3.4.0-dev.202605241634",
4
4
  "description": "React Native SDK for Nami - No-code paywall and onboarding flows with A/B testing.",
5
5
  "main": "index.ts",
6
6
  "types": "dist/index.d.ts",
@@ -63,7 +63,7 @@
63
63
  ]
64
64
  },
65
65
  "peerDependencies": {
66
- "@namiml/expo-nami-iap": "3.4.0-dev.202605201800",
66
+ "@namiml/expo-nami-iap": "3.4.0-dev.202605241634",
67
67
  "react": ">=18",
68
68
  "react-native": ">=0.73"
69
69
  },
@@ -21,7 +21,7 @@ Pod::Spec.new do |s|
21
21
  s.requires_arc = true
22
22
  s.swift_version = '5.0' # or your supported version
23
23
 
24
- s.dependency 'Nami', '3.4.0-dev.202605201800'
24
+ s.dependency 'Nami', '3.4.0-dev.202605241634'
25
25
 
26
26
  pod_target_xcconfig = {
27
27
  'DEFINES_MODULE' => 'YES',
@@ -1,3 +1,4 @@
1
+ import React, { useEffect, useState } from 'react';
1
2
  import { AppRegistry, View, StyleSheet } from 'react-native';
2
3
  import {
3
4
  TurboModuleRegistry,
@@ -12,6 +13,12 @@ const RNNamiOverlayControl: Spec =
12
13
 
13
14
  const emitter = new NativeEventEmitter(NativeModules.RNNamiOverlayControl);
14
15
 
16
+ // `NamiOverlayHost` is mounted in a separate RCTRootView, so it cannot share
17
+ // React context with the main app tree. We use a module-level singleton +
18
+ // listener set to cross that boundary.
19
+ let registeredContent: React.ComponentType | null = null;
20
+ const contentListeners = new Set<() => void>();
21
+
15
22
  export const NamiOverlayControl = {
16
23
  emitter,
17
24
 
@@ -43,9 +50,29 @@ export const NamiOverlayControl = {
43
50
  const sub = emitter.addListener('NamiOverlayResult', handler);
44
51
  return () => sub.remove();
45
52
  },
53
+
54
+ setOverlayContent(component: React.ComponentType | null): void {
55
+ registeredContent = component;
56
+ contentListeners.forEach((listener) => listener());
57
+ },
46
58
  };
47
59
 
48
60
  export default function NamiOverlayHost() {
61
+ const [Content, setContent] = useState<React.ComponentType | null>(
62
+ () => registeredContent,
63
+ );
64
+
65
+ useEffect(() => {
66
+ const listener = () => setContent(() => registeredContent);
67
+ contentListeners.add(listener);
68
+ return () => {
69
+ contentListeners.delete(listener);
70
+ };
71
+ }, []);
72
+
73
+ if (Content) {
74
+ return <Content />;
75
+ }
49
76
  return <View style={styles.overlay} />;
50
77
  }
51
78
 
@@ -1,14 +1,23 @@
1
1
  // Mock react-native BEFORE importing the SDK — `../Nami` resolves
2
2
  // the native module via TurboModuleRegistry at import time.
3
3
  const reset = jest.fn().mockResolvedValue(undefined);
4
+ // The wrapper reads `result?.success` (see Nami.ts), so the mock must
5
+ // return that shape — not a plain boolean.
6
+ const configure = jest.fn().mockResolvedValue({ success: true });
7
+ const sdkConfigured = jest.fn().mockResolvedValue(false);
4
8
  const sdkVersion = jest.fn().mockResolvedValue('test');
5
9
 
6
10
  jest.mock('react-native', () => ({
7
11
  TurboModuleRegistry: {
8
- getEnforcing: jest.fn(() => ({ reset, sdkVersion })),
12
+ getEnforcing: jest.fn(() => ({
13
+ reset,
14
+ configure,
15
+ sdkConfigured,
16
+ sdkVersion,
17
+ })),
9
18
  },
10
19
  NativeModules: {
11
- RNNami: { reset, sdkVersion },
20
+ RNNami: { reset, configure, sdkConfigured, sdkVersion },
12
21
  },
13
22
  }));
14
23
 
@@ -17,6 +26,7 @@ import { Nami } from '../Nami';
17
26
  describe('Nami.reset (React Native bridge)', () => {
18
27
  beforeEach(() => {
19
28
  reset.mockClear();
29
+ configure.mockClear();
20
30
  });
21
31
 
22
32
  it('exposes reset() as an async function on the JS surface', () => {
@@ -39,4 +49,49 @@ describe('Nami.reset (React Native bridge)', () => {
39
49
  reset.mockRejectedValueOnce(err);
40
50
  await expect(Nami.reset()).rejects.toThrow('native reset failed');
41
51
  });
52
+
53
+ // NAM-1086: pre-fix, calling Nami.reset() before Nami.configure() on
54
+ // Android crashed inside `NamiEntitlementManager`'s class-init block,
55
+ // which read `Nami.refs.context` (a `lateinit`). The native bridge's
56
+ // `try { Nami.reset(...) } catch (e: Throwable) { promise.reject(...) }`
57
+ // turned that into a JS-observable `ExceptionInInitializerError`
58
+ // rejection of the `Nami.reset()` promise. These tests pin the JS-side
59
+ // contract: regardless of order or repetition, the bridge invocation
60
+ // must resolve cleanly and the host app must not see a rejection.
61
+ describe('order-of-calls (NAM-1086)', () => {
62
+ it('does not reject when called before configure', async () => {
63
+ // No prior `Nami.configure()` — simulates the cold-start path the
64
+ // RN host app exercises when wiping persisted state on launch.
65
+ await expect(Nami.reset()).resolves.toBeUndefined();
66
+ expect(reset).toHaveBeenCalledTimes(1);
67
+ expect(configure).not.toHaveBeenCalled();
68
+ });
69
+
70
+ it('does not reject when called after configure', async () => {
71
+ await Nami.configure({} as any);
72
+ await expect(Nami.reset()).resolves.toBeUndefined();
73
+ expect(configure).toHaveBeenCalledTimes(1);
74
+ expect(reset).toHaveBeenCalledTimes(1);
75
+ });
76
+
77
+ it('is idempotent across repeated cold calls', async () => {
78
+ // The host app may bind `Nami.reset()` to a "Clear Data" button
79
+ // that the user can tap multiple times in a row — each tap must
80
+ // resolve cleanly.
81
+ await expect(Nami.reset()).resolves.toBeUndefined();
82
+ await expect(Nami.reset()).resolves.toBeUndefined();
83
+ await expect(Nami.reset()).resolves.toBeUndefined();
84
+ expect(reset).toHaveBeenCalledTimes(3);
85
+ });
86
+
87
+ it('does not reject when interleaved with configure', async () => {
88
+ // Realistic flow: reset cold → configure → reset again to wipe
89
+ // post-configure state. Each step must resolve.
90
+ await expect(Nami.reset()).resolves.toBeUndefined();
91
+ await expect(Nami.configure({} as any)).resolves.toBe(true);
92
+ await expect(Nami.reset()).resolves.toBeUndefined();
93
+ expect(reset).toHaveBeenCalledTimes(2);
94
+ expect(configure).toHaveBeenCalledTimes(1);
95
+ });
96
+ });
42
97
  });
package/src/version.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * Auto-generated file. Do not edit manually.
3
3
  * React Native Nami SDK version.
4
4
  */
5
- export const NAMI_REACT_NATIVE_VERSION = '3.4.0-dev.202605201800';
5
+ export const NAMI_REACT_NATIVE_VERSION = '3.4.0-dev.202605241634';
package/CHANGELOG.md DELETED
@@ -1 +0,0 @@
1
- # Changelog - React Native SDK