@billdog.io/react-native 1.0.0-beta.2

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.
Files changed (309) hide show
  1. package/BillDogReactNative.podspec +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +330 -0
  4. package/android/build.gradle +99 -0
  5. package/android/src/main/AndroidManifest.xml +1 -0
  6. package/android/src/main/kotlin/io/billdog/reactnative/BillDogABTestModule.kt +317 -0
  7. package/android/src/main/kotlin/io/billdog/reactnative/BillDogAnalyticsModule.kt +389 -0
  8. package/android/src/main/kotlin/io/billdog/reactnative/BillDogModule.kt +817 -0
  9. package/android/src/main/kotlin/io/billdog/reactnative/BillDogPaywallViewManager.kt +383 -0
  10. package/android/src/main/kotlin/io/billdog/reactnative/BillDogSurveyModule.kt +427 -0
  11. package/android/src/main/kotlin/io/billdog/reactnative/BillDogVirtualCurrencyModule.kt +407 -0
  12. package/android/src/main/kotlin/io/billdog/virtualcurrency/BillDogVirtualCurrencyPackage.kt +44 -0
  13. package/dist/AdvancedPaywallCache.d.ts +70 -0
  14. package/dist/AdvancedPaywallCache.js +344 -0
  15. package/dist/BillDog.d.ts +255 -0
  16. package/dist/BillDog.js +918 -0
  17. package/dist/BillDogConfiguration.d.ts +145 -0
  18. package/dist/BillDogConfiguration.js +229 -0
  19. package/dist/ComponentRenderer.d.ts +17 -0
  20. package/dist/ComponentRenderer.js +886 -0
  21. package/dist/LayoutEngine.d.ts +101 -0
  22. package/dist/LayoutEngine.js +394 -0
  23. package/dist/LocalizationResolver.d.ts +99 -0
  24. package/dist/LocalizationResolver.js +266 -0
  25. package/dist/NativePaywallView.d.ts +36 -0
  26. package/dist/NativePaywallView.js +59 -0
  27. package/dist/PaywallCache.d.ts +74 -0
  28. package/dist/PaywallCache.js +293 -0
  29. package/dist/PaywallStateManager.d.ts +62 -0
  30. package/dist/PaywallStateManager.js +147 -0
  31. package/dist/PaywallView.d.ts +42 -0
  32. package/dist/PaywallView.js +275 -0
  33. package/dist/ProductVariableProvider.d.ts +60 -0
  34. package/dist/ProductVariableProvider.js +239 -0
  35. package/dist/VariableResolver.d.ts +120 -0
  36. package/dist/VariableResolver.js +379 -0
  37. package/dist/WebViewPaywall.d.ts +75 -0
  38. package/dist/WebViewPaywall.js +360 -0
  39. package/dist/animations/presets.d.ts +49 -0
  40. package/dist/animations/presets.js +367 -0
  41. package/dist/components/AccoladeCpsComponent.d.ts +13 -0
  42. package/dist/components/AccoladeCpsComponent.js +248 -0
  43. package/dist/components/AccordionComponent.d.ts +10 -0
  44. package/dist/components/AccordionComponent.js +187 -0
  45. package/dist/components/AnimatedWrapper.d.ts +35 -0
  46. package/dist/components/AnimatedWrapper.js +277 -0
  47. package/dist/components/CarouselComponent.d.ts +23 -0
  48. package/dist/components/CarouselComponent.js +484 -0
  49. package/dist/components/ChartCurveComponent.d.ts +13 -0
  50. package/dist/components/ChartCurveComponent.js +333 -0
  51. package/dist/components/CheckboxComponent.d.ts +9 -0
  52. package/dist/components/CheckboxComponent.js +172 -0
  53. package/dist/components/ConfettiComponent.d.ts +9 -0
  54. package/dist/components/ConfettiComponent.js +172 -0
  55. package/dist/components/CountdownComponent.d.ts +16 -0
  56. package/dist/components/CountdownComponent.js +414 -0
  57. package/dist/components/DelayedXComponent.d.ts +12 -0
  58. package/dist/components/DelayedXComponent.js +240 -0
  59. package/dist/components/DrawerModalComponent.d.ts +20 -0
  60. package/dist/components/DrawerModalComponent.js +286 -0
  61. package/dist/components/GradientView.d.ts +38 -0
  62. package/dist/components/GradientView.js +126 -0
  63. package/dist/components/HapticFeedbackComponent.d.ts +16 -0
  64. package/dist/components/HapticFeedbackComponent.js +64 -0
  65. package/dist/components/HorizontalProductsComponent.d.ts +17 -0
  66. package/dist/components/HorizontalProductsComponent.js +284 -0
  67. package/dist/components/MultiChoiceGridComponent.d.ts +13 -0
  68. package/dist/components/MultiChoiceGridComponent.js +234 -0
  69. package/dist/components/NPSScaleComponent.d.ts +16 -0
  70. package/dist/components/NPSScaleComponent.js +260 -0
  71. package/dist/components/NavigationComponent.d.ts +26 -0
  72. package/dist/components/NavigationComponent.js +302 -0
  73. package/dist/components/OAuthButtonComponent.d.ts +20 -0
  74. package/dist/components/OAuthButtonComponent.js +237 -0
  75. package/dist/components/ProgressLoaderComponent.d.ts +13 -0
  76. package/dist/components/ProgressLoaderComponent.js +308 -0
  77. package/dist/components/ProgressStepperComponent.d.ts +18 -0
  78. package/dist/components/ProgressStepperComponent.js +228 -0
  79. package/dist/components/SegmentedPickerComponent.d.ts +10 -0
  80. package/dist/components/SegmentedPickerComponent.js +221 -0
  81. package/dist/components/ShareButtonsComponent.d.ts +21 -0
  82. package/dist/components/ShareButtonsComponent.js +265 -0
  83. package/dist/components/ShareCodeComponent.d.ts +16 -0
  84. package/dist/components/ShareCodeComponent.js +274 -0
  85. package/dist/components/SingleChoiceGridComponent.d.ts +13 -0
  86. package/dist/components/SingleChoiceGridComponent.js +210 -0
  87. package/dist/components/SliderComponent.d.ts +9 -0
  88. package/dist/components/SliderComponent.js +263 -0
  89. package/dist/components/StarRatingInputComponent.d.ts +20 -0
  90. package/dist/components/StarRatingInputComponent.js +198 -0
  91. package/dist/components/StickerCompositeComponent.d.ts +13 -0
  92. package/dist/components/StickerCompositeComponent.js +136 -0
  93. package/dist/components/TabsComponent.d.ts +19 -0
  94. package/dist/components/TabsComponent.js +431 -0
  95. package/dist/components/TextInputComponent.d.ts +18 -0
  96. package/dist/components/TextInputComponent.js +227 -0
  97. package/dist/components/TimelineComponent.d.ts +19 -0
  98. package/dist/components/TimelineComponent.js +104 -0
  99. package/dist/components/ToggleComponent.d.ts +14 -0
  100. package/dist/components/ToggleComponent.js +132 -0
  101. package/dist/components/VerticalProductsComponent.d.ts +17 -0
  102. package/dist/components/VerticalProductsComponent.js +311 -0
  103. package/dist/dynamicValues.d.ts +36 -0
  104. package/dist/dynamicValues.js +142 -0
  105. package/dist/filters/dateFilters.d.ts +50 -0
  106. package/dist/filters/dateFilters.js +390 -0
  107. package/dist/filters/index.d.ts +18 -0
  108. package/dist/filters/index.js +253 -0
  109. package/dist/hooks/useOfferings.d.ts +76 -0
  110. package/dist/hooks/useOfferings.js +99 -0
  111. package/dist/hooks/usePaywall.d.ts +27 -0
  112. package/dist/hooks/usePaywall.js +114 -0
  113. package/dist/hooks/usePlacement.d.ts +34 -0
  114. package/dist/hooks/usePlacement.js +89 -0
  115. package/dist/hooks/useSubscription.d.ts +40 -0
  116. package/dist/hooks/useSubscription.js +97 -0
  117. package/dist/index.d.ts +98 -0
  118. package/dist/index.js +198 -0
  119. package/dist/models/BillDogError.d.ts +40 -0
  120. package/dist/models/BillDogError.js +11 -0
  121. package/dist/models/CacheFetchPolicy.d.ts +8 -0
  122. package/dist/models/CacheFetchPolicy.js +4 -0
  123. package/dist/models/CustomProduct.d.ts +32 -0
  124. package/dist/models/CustomProduct.js +15 -0
  125. package/dist/models/CustomerInfo.d.ts +17 -0
  126. package/dist/models/CustomerInfo.js +11 -0
  127. package/dist/models/Delegate.d.ts +20 -0
  128. package/dist/models/Delegate.js +11 -0
  129. package/dist/models/EntitlementInfo.d.ts +19 -0
  130. package/dist/models/EntitlementInfo.js +2 -0
  131. package/dist/models/Placement.d.ts +54 -0
  132. package/dist/models/Placement.js +15 -0
  133. package/dist/models/PurchaseParams.d.ts +39 -0
  134. package/dist/models/PurchaseParams.js +60 -0
  135. package/dist/models/PurchaseResult.d.ts +30 -0
  136. package/dist/models/PurchaseResult.js +15 -0
  137. package/dist/models/RestoreResult.d.ts +30 -0
  138. package/dist/models/RestoreResult.js +22 -0
  139. package/dist/models/RestoredItem.d.ts +26 -0
  140. package/dist/models/RestoredItem.js +13 -0
  141. package/dist/modules/ABTest.d.ts +65 -0
  142. package/dist/modules/ABTest.js +113 -0
  143. package/dist/modules/Analytics.d.ts +91 -0
  144. package/dist/modules/Analytics.js +182 -0
  145. package/dist/modules/Survey.d.ts +65 -0
  146. package/dist/modules/Survey.js +82 -0
  147. package/dist/modules/VirtualCurrency.d.ts +63 -0
  148. package/dist/modules/VirtualCurrency.js +147 -0
  149. package/dist/murmur.d.ts +23 -0
  150. package/dist/murmur.js +104 -0
  151. package/dist/notifications/LiveActivitiesManager.d.ts +47 -0
  152. package/dist/notifications/LiveActivitiesManager.js +106 -0
  153. package/dist/notifications/NotificationScheduler.d.ts +55 -0
  154. package/dist/notifications/NotificationScheduler.js +199 -0
  155. package/dist/notifications/PurchaseNotificationHandler.d.ts +24 -0
  156. package/dist/notifications/PurchaseNotificationHandler.js +55 -0
  157. package/dist/notifications/useDeviceToken.d.ts +36 -0
  158. package/dist/notifications/useDeviceToken.js +97 -0
  159. package/dist/notifications/useLiveActivity.d.ts +39 -0
  160. package/dist/notifications/useLiveActivity.js +104 -0
  161. package/dist/review/ReviewAnalytics.d.ts +34 -0
  162. package/dist/review/ReviewAnalytics.js +73 -0
  163. package/dist/review/ReviewManager.d.ts +83 -0
  164. package/dist/review/ReviewManager.js +339 -0
  165. package/dist/review/ReviewTimingManager.d.ts +82 -0
  166. package/dist/review/ReviewTimingManager.js +224 -0
  167. package/dist/survey-core/types.d.ts +215 -0
  168. package/dist/survey-core/types.js +8 -0
  169. package/dist/types.d.ts +438 -0
  170. package/dist/types.js +158 -0
  171. package/dist/utils/ApiClient.d.ts +65 -0
  172. package/dist/utils/ApiClient.js +132 -0
  173. package/dist/utils/CSSConverter.d.ts +49 -0
  174. package/dist/utils/CSSConverter.js +470 -0
  175. package/dist/utils/animations.d.ts +41 -0
  176. package/dist/utils/animations.js +194 -0
  177. package/dist/utils/calendarIntegration.d.ts +36 -0
  178. package/dist/utils/calendarIntegration.js +89 -0
  179. package/dist/utils/configurationResolver.d.ts +23 -0
  180. package/dist/utils/configurationResolver.js +78 -0
  181. package/dist/utils/deviceDetection.d.ts +38 -0
  182. package/dist/utils/deviceDetection.js +59 -0
  183. package/dist/utils/hapticFeedback.d.ts +17 -0
  184. package/dist/utils/hapticFeedback.js +53 -0
  185. package/dist/utils/iconMapper.d.ts +26 -0
  186. package/dist/utils/iconMapper.js +113 -0
  187. package/dist/utils/notificationPermissions.d.ts +13 -0
  188. package/dist/utils/notificationPermissions.js +51 -0
  189. package/dist/utils/priceFormatting.d.ts +19 -0
  190. package/dist/utils/priceFormatting.js +99 -0
  191. package/dist/utils/shareSheet.d.ts +17 -0
  192. package/dist/utils/shareSheet.js +42 -0
  193. package/dist/utils/subscriptionUpgrade.d.ts +84 -0
  194. package/dist/utils/subscriptionUpgrade.js +100 -0
  195. package/dist/valueUtils.d.ts +48 -0
  196. package/dist/valueUtils.js +129 -0
  197. package/dist/xFlowTags.d.ts +18 -0
  198. package/dist/xFlowTags.js +385 -0
  199. package/ios/BillDogABTest.m +50 -0
  200. package/ios/BillDogABTest.swift +188 -0
  201. package/ios/BillDogAnalytics.m +105 -0
  202. package/ios/BillDogAnalytics.swift +324 -0
  203. package/ios/BillDogBridge.m +222 -0
  204. package/ios/BillDogBridge.swift +715 -0
  205. package/ios/BillDogLiveActivities.m +41 -0
  206. package/ios/BillDogLiveActivities.swift +104 -0
  207. package/ios/BillDogPaywallViewManager.m +39 -0
  208. package/ios/BillDogPaywallViewManager.swift +361 -0
  209. package/ios/BillDogSurvey.m +34 -0
  210. package/ios/BillDogSurvey.swift +257 -0
  211. package/ios/BillDogVirtualCurrencyBridge.m +63 -0
  212. package/ios/BillDogVirtualCurrencyBridge.swift +511 -0
  213. package/junit.xml +25 -0
  214. package/package.json +61 -0
  215. package/react-native.config.js +13 -0
  216. package/src/AdvancedPaywallCache.ts +433 -0
  217. package/src/BillDog.ts +983 -0
  218. package/src/BillDogConfiguration.ts +318 -0
  219. package/src/ComponentRenderer.tsx +1325 -0
  220. package/src/LayoutEngine.ts +459 -0
  221. package/src/LocalizationResolver.ts +311 -0
  222. package/src/NativePaywallView.tsx +132 -0
  223. package/src/PaywallCache.ts +358 -0
  224. package/src/PaywallStateManager.ts +154 -0
  225. package/src/PaywallView.tsx +328 -0
  226. package/src/ProductVariableProvider.ts +282 -0
  227. package/src/VariableResolver.ts +516 -0
  228. package/src/WebViewPaywall.tsx +454 -0
  229. package/src/animations/presets.ts +421 -0
  230. package/src/components/AccoladeCpsComponent.tsx +259 -0
  231. package/src/components/AccordionComponent.tsx +213 -0
  232. package/src/components/AnimatedWrapper.tsx +316 -0
  233. package/src/components/CarouselComponent.tsx +615 -0
  234. package/src/components/ChartCurveComponent.tsx +452 -0
  235. package/src/components/CheckboxComponent.tsx +185 -0
  236. package/src/components/ConfettiComponent.tsx +178 -0
  237. package/src/components/CountdownComponent.tsx +454 -0
  238. package/src/components/DelayedXComponent.tsx +266 -0
  239. package/src/components/DrawerModalComponent.tsx +324 -0
  240. package/src/components/GradientView.tsx +129 -0
  241. package/src/components/HapticFeedbackComponent.tsx +39 -0
  242. package/src/components/HorizontalProductsComponent.tsx +329 -0
  243. package/src/components/MultiChoiceGridComponent.tsx +265 -0
  244. package/src/components/NPSScaleComponent.tsx +294 -0
  245. package/src/components/NavigationComponent.tsx +329 -0
  246. package/src/components/OAuthButtonComponent.tsx +266 -0
  247. package/src/components/ProgressLoaderComponent.tsx +366 -0
  248. package/src/components/ProgressStepperComponent.tsx +250 -0
  249. package/src/components/SegmentedPickerComponent.tsx +245 -0
  250. package/src/components/ShareButtonsComponent.tsx +299 -0
  251. package/src/components/ShareCodeComponent.tsx +303 -0
  252. package/src/components/SingleChoiceGridComponent.tsx +229 -0
  253. package/src/components/SliderComponent.tsx +280 -0
  254. package/src/components/StarRatingInputComponent.tsx +223 -0
  255. package/src/components/StickerCompositeComponent.tsx +168 -0
  256. package/src/components/TabsComponent.tsx +496 -0
  257. package/src/components/TextInputComponent.tsx +258 -0
  258. package/src/components/TimelineComponent.tsx +129 -0
  259. package/src/components/ToggleComponent.tsx +142 -0
  260. package/src/components/VerticalProductsComponent.tsx +357 -0
  261. package/src/dynamicValues.ts +180 -0
  262. package/src/filters/dateFilters.ts +411 -0
  263. package/src/filters/index.ts +281 -0
  264. package/src/hooks/useOfferings.ts +192 -0
  265. package/src/hooks/usePaywall.ts +154 -0
  266. package/src/hooks/usePlacement.ts +143 -0
  267. package/src/hooks/useSubscription.ts +154 -0
  268. package/src/index.ts +245 -0
  269. package/src/models/BillDogError.ts +24 -0
  270. package/src/models/CacheFetchPolicy.ts +13 -0
  271. package/src/models/CustomProduct.ts +33 -0
  272. package/src/models/CustomerInfo.ts +26 -0
  273. package/src/models/Delegate.ts +22 -0
  274. package/src/models/EntitlementInfo.ts +27 -0
  275. package/src/models/Placement.ts +50 -0
  276. package/src/models/PurchaseParams.ts +81 -0
  277. package/src/models/PurchaseResult.ts +32 -0
  278. package/src/models/RestoreResult.ts +27 -0
  279. package/src/models/RestoredItem.ts +25 -0
  280. package/src/modules/ABTest.ts +131 -0
  281. package/src/modules/Analytics.ts +227 -0
  282. package/src/modules/Survey.ts +126 -0
  283. package/src/modules/VirtualCurrency.ts +223 -0
  284. package/src/murmur.ts +102 -0
  285. package/src/notifications/LiveActivitiesManager.ts +140 -0
  286. package/src/notifications/NotificationScheduler.ts +292 -0
  287. package/src/notifications/PurchaseNotificationHandler.ts +67 -0
  288. package/src/notifications/useDeviceToken.ts +116 -0
  289. package/src/notifications/useLiveActivity.ts +125 -0
  290. package/src/review/ReviewAnalytics.ts +89 -0
  291. package/src/review/ReviewManager.ts +355 -0
  292. package/src/review/ReviewTimingManager.ts +265 -0
  293. package/src/survey-core/types.ts +244 -0
  294. package/src/types/ambient.d.ts +9 -0
  295. package/src/types.ts +680 -0
  296. package/src/utils/ApiClient.ts +163 -0
  297. package/src/utils/CSSConverter.ts +552 -0
  298. package/src/utils/animations.ts +232 -0
  299. package/src/utils/calendarIntegration.ts +113 -0
  300. package/src/utils/configurationResolver.ts +106 -0
  301. package/src/utils/deviceDetection.ts +83 -0
  302. package/src/utils/hapticFeedback.ts +55 -0
  303. package/src/utils/iconMapper.ts +102 -0
  304. package/src/utils/notificationPermissions.ts +48 -0
  305. package/src/utils/priceFormatting.ts +119 -0
  306. package/src/utils/shareSheet.ts +55 -0
  307. package/src/utils/subscriptionUpgrade.ts +183 -0
  308. package/src/valueUtils.ts +108 -0
  309. package/src/xFlowTags.ts +463 -0
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Client-side variable resolver for React Native
3
+ * Resolves ${variable} and {{variable}} placeholders at runtime
4
+ * Supports localization integration for localized variables
5
+ */
6
+ import { LocalizationResolver } from './LocalizationResolver';
7
+ import { PaywallConfiguration, PaywallComponent } from './types';
8
+ import { PackageInput, TrialEligibilityInput } from './ProductVariableProvider';
9
+ export interface DeviceInfo {
10
+ osName?: string;
11
+ osVersion?: string;
12
+ platform?: string;
13
+ deviceModel?: string;
14
+ screenWidth?: number;
15
+ screenHeight?: number;
16
+ locale?: string;
17
+ timezone?: string;
18
+ interfaceStyle?: 'light' | 'dark' | 'unspecified';
19
+ batteryLevel?: number;
20
+ isLowPowerMode?: boolean;
21
+ networkType?: string;
22
+ carrierName?: string;
23
+ }
24
+ export declare class VariableResolver {
25
+ private allVariables;
26
+ private localizationResolver?;
27
+ private variableLocalizations?;
28
+ private currentLocale?;
29
+ constructor(configVariables?: Record<string, string> | null, runtimeVariables?: Record<string, any>, localizationResolver?: LocalizationResolver, variableLocalizations?: Record<string, Record<string, string>>, currentLocale?: string);
30
+ /**
31
+ * Resolve a single string with variables
32
+ */
33
+ resolve(text?: string | null): string;
34
+ /**
35
+ * Resolve a variable path to its raw (un-stringified) value.
36
+ * Flat-key-first (grammar §4): a literal key matching the whole path wins.
37
+ * Returns undefined when unresolved.
38
+ */
39
+ private resolveRawValue;
40
+ /**
41
+ * Resolve nested variable paths like "offering.monthly"
42
+ */
43
+ private resolveVariablePath;
44
+ /**
45
+ * Resolve all text properties in a component
46
+ * Applies localization for LOCALIZABLE_PROPERTIES (matching Android/iOS parity)
47
+ */
48
+ resolveComponent(component: PaywallComponent): PaywallComponent;
49
+ /**
50
+ * Resolve all components in a configuration
51
+ */
52
+ resolveConfiguration(config: PaywallConfiguration): PaywallConfiguration;
53
+ /**
54
+ * Build device variable map from DeviceInfo — single source of truth.
55
+ * Used by withDeviceInfo(), withFullContext(), withProducts().
56
+ * Maps RN DeviceInfo field names to canonical SDK key names.
57
+ */
58
+ static buildDeviceVariables(deviceInfo: DeviceInfo): Record<string, any>;
59
+ /**
60
+ * Create resolver with offering variables injected
61
+ */
62
+ static withOfferings(config: PaywallConfiguration, offerings: Record<string, string>): VariableResolver;
63
+ /**
64
+ * Create resolver with device info variables
65
+ * Maps device info to namespaced variables (device.*)
66
+ */
67
+ static withDeviceInfo(config: PaywallConfiguration, deviceInfo: DeviceInfo): VariableResolver;
68
+ /**
69
+ * Create resolver with localization support
70
+ */
71
+ static withLocalization(config: PaywallConfiguration, locale?: string): VariableResolver;
72
+ /**
73
+ * Create resolver with full context (offerings, device info, localization)
74
+ */
75
+ static withFullContext(config: PaywallConfiguration, offerings: Record<string, string>, deviceInfo: DeviceInfo): VariableResolver;
76
+ /**
77
+ * Create resolver with user eligibility variables
78
+ * Enables {{user.isEligibleForIntro}}, {{user.hasUsedTrial}}, etc.
79
+ */
80
+ static withEligibility(config: PaywallConfiguration, isEligibleForIntro?: boolean, hasUsedTrial?: boolean, isEligibleForPromo?: boolean): VariableResolver;
81
+ /**
82
+ * Create resolver with product/package variables
83
+ * Enables {{product.price}}, {{selectedProduct.*}}, {{products.N.*}},
84
+ * {{primary.*}}, {{monthly.*}}, etc. (40+ variable keys per package)
85
+ *
86
+ * Delegates to ProductVariableProvider for full parity with Android.
87
+ */
88
+ static withProducts(config: PaywallConfiguration, packages: PackageInput[], selectedIndex?: number, trialEligibility?: Record<string, TrialEligibilityInput>, deviceInfo?: DeviceInfo): VariableResolver;
89
+ /**
90
+ * Create resolver with an Offering's packages
91
+ * Convenience wrapper around withProducts
92
+ */
93
+ static withOffering(config: PaywallConfiguration, offering: {
94
+ packages: PackageInput[];
95
+ }, selectedIndex?: number, trialEligibility?: Record<string, TrialEligibilityInput>, deviceInfo?: DeviceInfo): VariableResolver;
96
+ /**
97
+ * Build the FULL render-time variable bag (device + product/offer/user) — the
98
+ * React-Native analog of iOS/Android `VariableResolver.withOffering(...).variables`.
99
+ * Fed to `processFullComponent` so Dynamic Values conditions on
100
+ * `products.*` / `selectedProduct.*` / `offer.*` / `user.*` actually resolve on
101
+ * RN (previously the render bag was device + `config.variables` only, so every
102
+ * product/user DV condition silently rendered `default`).
103
+ *
104
+ * `selectedIndex` is 0 for now — parity with the native Phase-2 render pass,
105
+ * which is not yet selection-reactive (Phase 2b). `appUserId`/`customerInfo` are
106
+ * passed in by the caller (`BillDog.getAppUserId()` + `customerInfoEmitter.latest`,
107
+ * both sync) to avoid a VariableResolver→BillDog import cycle.
108
+ */
109
+ static buildRenderVariables(config: PaywallConfiguration, deviceInfo: DeviceInfo, opts?: {
110
+ selectedIndex?: number;
111
+ appUserId?: string | null;
112
+ customerInfo?: {
113
+ entitlements?: Array<{
114
+ identifier: string;
115
+ isActive: boolean;
116
+ }>;
117
+ activeSubscriptions?: string[];
118
+ } | null;
119
+ }): Record<string, any>;
120
+ }
@@ -0,0 +1,379 @@
1
+ "use strict";
2
+ /**
3
+ * Client-side variable resolver for React Native
4
+ * Resolves ${variable} and {{variable}} placeholders at runtime
5
+ * Supports localization integration for localized variables
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.VariableResolver = void 0;
9
+ const LocalizationResolver_1 = require("./LocalizationResolver");
10
+ const xFlowTags_1 = require("./xFlowTags");
11
+ const filters_1 = require("./filters");
12
+ const ProductVariableProvider_1 = require("./ProductVariableProvider");
13
+ const murmur_1 = require("./murmur");
14
+ /**
15
+ * Get device locale with safe fallback for React Native
16
+ */
17
+ function deviceLocale() {
18
+ try {
19
+ return Intl.DateTimeFormat().resolvedOptions().locale;
20
+ }
21
+ catch {
22
+ return 'en';
23
+ }
24
+ }
25
+ /** Properties eligible for localization (matches Android/iOS LOCALIZABLE_PROPERTIES) */
26
+ const LOCALIZABLE_PROPERTIES = new Set([
27
+ 'text', 'title', 'subtitle', 'label', 'placeholder',
28
+ 'buttonText', 'ctaText', 'description', 'content',
29
+ 'headerText', 'footerText', 'disclaimer',
30
+ ]);
31
+ class VariableResolver {
32
+ constructor(configVariables, runtimeVariables = {}, localizationResolver, variableLocalizations, currentLocale) {
33
+ this.allVariables = {
34
+ ...configVariables,
35
+ ...runtimeVariables,
36
+ };
37
+ this.localizationResolver = localizationResolver;
38
+ this.variableLocalizations = variableLocalizations;
39
+ this.currentLocale = currentLocale;
40
+ }
41
+ /**
42
+ * Resolve a single string with variables
43
+ */
44
+ resolve(text) {
45
+ if (!text)
46
+ return text !== null && text !== void 0 ? text : '';
47
+ let result = text;
48
+ // Local copy — xFlow assigns must not leak across resolve() calls
49
+ let resolveVars = { ...this.allVariables };
50
+ // Process xFlow {% %} tags before {{ }} interpolation
51
+ if (result.includes('{%')) {
52
+ const xFlowResult = (0, xFlowTags_1.processXFlowTags)(result, resolveVars);
53
+ result = xFlowResult.text;
54
+ // Merge assigned variables into local context only
55
+ resolveVars = { ...resolveVars, ...xFlowResult.variables };
56
+ }
57
+ // Resolve ${variable} syntax
58
+ result = result.replace(/\$\{([\w.]+)\}/g, (_, varName) => {
59
+ return this.resolveVariablePath(varName, resolveVars);
60
+ });
61
+ // Resolve {{ variable }} and {{ variable | filter: arg }} syntax
62
+ result = result.replace(/\{\{\s*([\w.]+)(?:\s*\|\s*(.+?))?\s*\}\}/g, (match, varName, filterChain) => {
63
+ const name = varName.trim();
64
+ if (!filterChain) {
65
+ return this.resolveVariablePath(name, resolveVars);
66
+ }
67
+ // Filter chains operate on the raw value, bypassing localization.
68
+ const raw = this.resolveRawValue(name, resolveVars);
69
+ if (raw === undefined)
70
+ return match;
71
+ let value = raw;
72
+ try {
73
+ value = (0, filters_1.applyFilterChain)(raw, filterChain.trim());
74
+ }
75
+ catch (error) {
76
+ console.warn('[VariableResolver] Filter chain failed:', error);
77
+ }
78
+ return value !== undefined && value !== null ? String(value) : match;
79
+ });
80
+ return result;
81
+ }
82
+ /**
83
+ * Resolve a variable path to its raw (un-stringified) value.
84
+ * Flat-key-first (grammar §4): a literal key matching the whole path wins.
85
+ * Returns undefined when unresolved.
86
+ */
87
+ resolveRawValue(path, from) {
88
+ if (path in from)
89
+ return from[path];
90
+ const parts = path.split('.');
91
+ let current = from[parts[0]];
92
+ for (let i = 1; i < parts.length; i++) {
93
+ if (current && typeof current === 'object') {
94
+ current = current[parts[i]];
95
+ }
96
+ else {
97
+ return undefined;
98
+ }
99
+ }
100
+ return current;
101
+ }
102
+ /**
103
+ * Resolve nested variable paths like "offering.monthly"
104
+ */
105
+ resolveVariablePath(path, from) {
106
+ var _a, _b;
107
+ const vars = from !== null && from !== void 0 ? from : this.allVariables;
108
+ // Priority 1: Check variableLocalizations[path][locale]
109
+ if (this.variableLocalizations && this.currentLocale) {
110
+ const localeMap = this.variableLocalizations[path];
111
+ if (localeMap) {
112
+ const localizedValue = (_b = (_a = localeMap[this.currentLocale]) !== null && _a !== void 0 ? _a : localeMap[this.currentLocale.split('-')[0]]) !== null && _b !== void 0 ? _b : localeMap[this.currentLocale.split('_')[0]];
113
+ if (localizedValue !== undefined) {
114
+ return localizedValue;
115
+ }
116
+ }
117
+ }
118
+ // Priority 2: Direct lookup
119
+ if (path in vars) {
120
+ return String(vars[path]);
121
+ }
122
+ // Priority 3: Try nested path resolution
123
+ const parts = path.split('.');
124
+ let current = undefined;
125
+ for (const part of parts) {
126
+ if (current === undefined) {
127
+ current = vars[part];
128
+ }
129
+ else if (typeof current === 'object' && current !== null) {
130
+ current = current[part];
131
+ }
132
+ else {
133
+ current = undefined;
134
+ break;
135
+ }
136
+ }
137
+ if (current !== undefined) {
138
+ return String(current);
139
+ }
140
+ // Return empty string for unresolved variables to match the unified parity
141
+ // contract: iOS VariableResolver.swift:177 and Android
142
+ // VariableResolver.kt:147 both substitute "". A `${path}` placeholder is
143
+ // editor-mode behavior and must not leak into runtime — an unresolved
144
+ // variable would otherwise render as literal `${user.name}` on-device.
145
+ //
146
+ // This fix existed in the legacy packages/react-native resolver and was
147
+ // lost when the thin bridge replaced it (ba6a68d86); the conformance
148
+ // corpus caught the regression. Warn rather than log so unresolved
149
+ // variables stay visible in production, matching both native SDKs.
150
+ console.warn(`[VariableResolver] Variable not resolved: ${path} — substituting empty string`);
151
+ return '';
152
+ }
153
+ /**
154
+ * Resolve all text properties in a component
155
+ * Applies localization for LOCALIZABLE_PROPERTIES (matching Android/iOS parity)
156
+ */
157
+ resolveComponent(component) {
158
+ if (!component)
159
+ return component;
160
+ const resolvedComponent = { ...component };
161
+ // Resolve properties
162
+ if (component.properties) {
163
+ const resolvedProperties = {};
164
+ for (const [key, value] of Object.entries(component.properties)) {
165
+ if (typeof value === 'string') {
166
+ const variableResolved = this.resolve(value);
167
+ // Apply localization for localizable properties (matching Android/iOS)
168
+ if (this.localizationResolver
169
+ && LOCALIZABLE_PROPERTIES.has(key)
170
+ && !this.localizationResolver.isBaseLocale()) {
171
+ resolvedProperties[key] = this.localizationResolver.resolve(`${component.id}.${key}`, variableResolved);
172
+ }
173
+ else {
174
+ resolvedProperties[key] = variableResolved;
175
+ }
176
+ }
177
+ else {
178
+ resolvedProperties[key] = value;
179
+ }
180
+ }
181
+ resolvedComponent.properties = resolvedProperties;
182
+ }
183
+ // Recursively resolve children
184
+ if (component.children && Array.isArray(component.children)) {
185
+ resolvedComponent.children = component.children.map((child) => this.resolveComponent(child));
186
+ }
187
+ return resolvedComponent;
188
+ }
189
+ /**
190
+ * Resolve all components in a configuration
191
+ */
192
+ resolveConfiguration(config) {
193
+ if (!config)
194
+ return config;
195
+ const resolvedConfig = { ...config };
196
+ if (config.components && Array.isArray(config.components)) {
197
+ resolvedConfig.components = config.components.map((comp) => this.resolveComponent(comp));
198
+ }
199
+ return resolvedConfig;
200
+ }
201
+ // ─── Device Variable Builder (single source of truth) ──────────────────────
202
+ /**
203
+ * Build device variable map from DeviceInfo — single source of truth.
204
+ * Used by withDeviceInfo(), withFullContext(), withProducts().
205
+ * Maps RN DeviceInfo field names to canonical SDK key names.
206
+ */
207
+ static buildDeviceVariables(deviceInfo) {
208
+ const vars = {};
209
+ // Flat keys for direct access (canonical names matching Android/iOS)
210
+ if (deviceInfo.osName)
211
+ vars['device.osName'] = deviceInfo.osName;
212
+ if (deviceInfo.osVersion)
213
+ vars['device.osVersion'] = deviceInfo.osVersion;
214
+ if (deviceInfo.platform)
215
+ vars['device.platform'] = deviceInfo.platform;
216
+ if (deviceInfo.deviceModel)
217
+ vars['device.deviceModel'] = deviceInfo.deviceModel;
218
+ if (deviceInfo.screenWidth !== undefined)
219
+ vars['device.screenWidth'] = deviceInfo.screenWidth;
220
+ if (deviceInfo.screenHeight !== undefined)
221
+ vars['device.screenHeight'] = deviceInfo.screenHeight;
222
+ if (deviceInfo.locale)
223
+ vars['device.locale'] = deviceInfo.locale;
224
+ if (deviceInfo.timezone)
225
+ vars['device.timezone'] = deviceInfo.timezone;
226
+ if (deviceInfo.interfaceStyle)
227
+ vars['device.interfaceStyle'] = deviceInfo.interfaceStyle;
228
+ if (deviceInfo.batteryLevel !== undefined)
229
+ vars['device.batteryLevel'] = deviceInfo.batteryLevel;
230
+ // Map RN field name to canonical SDK key name
231
+ if (deviceInfo.isLowPowerMode !== undefined)
232
+ vars['device.isLowPowerModeEnabled'] = deviceInfo.isLowPowerMode;
233
+ if (deviceInfo.networkType)
234
+ vars['device.radioType'] = deviceInfo.networkType;
235
+ if (deviceInfo.carrierName)
236
+ vars['device.carrierName'] = deviceInfo.carrierName;
237
+ // Nested object for dot-path resolution (includes screenWidth/Height)
238
+ vars.device = {
239
+ osName: deviceInfo.osName,
240
+ osVersion: deviceInfo.osVersion,
241
+ platform: deviceInfo.platform,
242
+ deviceModel: deviceInfo.deviceModel,
243
+ locale: deviceInfo.locale,
244
+ timezone: deviceInfo.timezone,
245
+ interfaceStyle: deviceInfo.interfaceStyle,
246
+ screenWidth: deviceInfo.screenWidth,
247
+ screenHeight: deviceInfo.screenHeight,
248
+ };
249
+ return vars;
250
+ }
251
+ // ─── Factory Methods ───────────────────────────────────────────────────────
252
+ /**
253
+ * Create resolver with offering variables injected
254
+ */
255
+ static withOfferings(config, offerings) {
256
+ return new VariableResolver(config.variables, offerings, LocalizationResolver_1.LocalizationResolver.fromConfiguration(config), config.variableLocalizations, deviceLocale());
257
+ }
258
+ /**
259
+ * Create resolver with device info variables
260
+ * Maps device info to namespaced variables (device.*)
261
+ */
262
+ static withDeviceInfo(config, deviceInfo) {
263
+ var _a;
264
+ return new VariableResolver(config.variables, VariableResolver.buildDeviceVariables(deviceInfo), LocalizationResolver_1.LocalizationResolver.fromConfiguration(config), config.variableLocalizations, (_a = deviceInfo.locale) !== null && _a !== void 0 ? _a : deviceLocale());
265
+ }
266
+ /**
267
+ * Create resolver with localization support
268
+ */
269
+ static withLocalization(config, locale) {
270
+ const locResolver = locale
271
+ ? LocalizationResolver_1.LocalizationResolver.withLocale(config, locale)
272
+ : LocalizationResolver_1.LocalizationResolver.withDeviceLocale(config);
273
+ return new VariableResolver(config.variables, {}, locResolver, config.variableLocalizations, locale !== null && locale !== void 0 ? locale : deviceLocale());
274
+ }
275
+ /**
276
+ * Create resolver with full context (offerings, device info, localization)
277
+ */
278
+ static withFullContext(config, offerings, deviceInfo) {
279
+ var _a;
280
+ const runtimeVars = {
281
+ ...VariableResolver.buildDeviceVariables(deviceInfo),
282
+ ...offerings,
283
+ };
284
+ return new VariableResolver(config.variables, runtimeVars, LocalizationResolver_1.LocalizationResolver.withDeviceLocale(config), config.variableLocalizations, (_a = deviceInfo.locale) !== null && _a !== void 0 ? _a : deviceLocale());
285
+ }
286
+ /**
287
+ * Create resolver with user eligibility variables
288
+ * Enables {{user.isEligibleForIntro}}, {{user.hasUsedTrial}}, etc.
289
+ */
290
+ static withEligibility(config, isEligibleForIntro = true, hasUsedTrial = false, isEligibleForPromo = false) {
291
+ const eligibilityVars = {
292
+ 'user.isEligibleForIntro': isEligibleForIntro,
293
+ 'user.hasUsedTrial': hasUsedTrial,
294
+ 'user.isEligibleForPromo': isEligibleForPromo,
295
+ user: {
296
+ isEligibleForIntro,
297
+ hasUsedTrial,
298
+ isEligibleForPromo,
299
+ },
300
+ };
301
+ return new VariableResolver(config.variables, eligibilityVars, LocalizationResolver_1.LocalizationResolver.fromConfiguration(config), config.variableLocalizations, deviceLocale());
302
+ }
303
+ /**
304
+ * Create resolver with product/package variables
305
+ * Enables {{product.price}}, {{selectedProduct.*}}, {{products.N.*}},
306
+ * {{primary.*}}, {{monthly.*}}, etc. (40+ variable keys per package)
307
+ *
308
+ * Delegates to ProductVariableProvider for full parity with Android.
309
+ */
310
+ static withProducts(config, packages, selectedIndex = 0, trialEligibility, deviceInfo) {
311
+ var _a;
312
+ const provider = new ProductVariableProvider_1.ProductVariableProvider(packages, trialEligibility);
313
+ const productVars = {};
314
+ // Selected product → product.* prefix
315
+ Object.assign(productVars, provider.getVariablesForProduct(selectedIndex));
316
+ // selectedProduct.* mirror
317
+ Object.assign(productVars, provider.getSelectedProductVariables(selectedIndex));
318
+ // products.N.* indexed access
319
+ Object.assign(productVars, provider.getAllProductVariables());
320
+ productVars['packages.count'] = packages.length;
321
+ // Convenience aliases (primary.*, secondary.*, monthly.*, annual.*, etc.)
322
+ Object.assign(productVars, provider.getConvenienceVariables());
323
+ // Add device info if provided — use canonical buildDeviceVariables
324
+ if (deviceInfo) {
325
+ Object.assign(productVars, VariableResolver.buildDeviceVariables(deviceInfo));
326
+ }
327
+ return new VariableResolver(config.variables, productVars, LocalizationResolver_1.LocalizationResolver.fromConfiguration(config), config.variableLocalizations, (_a = deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.locale) !== null && _a !== void 0 ? _a : deviceLocale());
328
+ }
329
+ /**
330
+ * Create resolver with an Offering's packages
331
+ * Convenience wrapper around withProducts
332
+ */
333
+ static withOffering(config, offering, selectedIndex = 0, trialEligibility, deviceInfo) {
334
+ return VariableResolver.withProducts(config, offering.packages, selectedIndex, trialEligibility, deviceInfo);
335
+ }
336
+ /**
337
+ * Build the FULL render-time variable bag (device + product/offer/user) — the
338
+ * React-Native analog of iOS/Android `VariableResolver.withOffering(...).variables`.
339
+ * Fed to `processFullComponent` so Dynamic Values conditions on
340
+ * `products.*` / `selectedProduct.*` / `offer.*` / `user.*` actually resolve on
341
+ * RN (previously the render bag was device + `config.variables` only, so every
342
+ * product/user DV condition silently rendered `default`).
343
+ *
344
+ * `selectedIndex` is 0 for now — parity with the native Phase-2 render pass,
345
+ * which is not yet selection-reactive (Phase 2b). `appUserId`/`customerInfo` are
346
+ * passed in by the caller (`BillDog.getAppUserId()` + `customerInfoEmitter.latest`,
347
+ * both sync) to avoid a VariableResolver→BillDog import cycle.
348
+ */
349
+ static buildRenderVariables(config, deviceInfo, opts) {
350
+ var _a, _b, _c, _d, _e;
351
+ const selectedIndex = (_a = opts === null || opts === void 0 ? void 0 : opts.selectedIndex) !== null && _a !== void 0 ? _a : 0;
352
+ const bag = { ...VariableResolver.buildDeviceVariables(deviceInfo) };
353
+ const packages = config.packages;
354
+ if (Array.isArray(packages) && packages.length > 0) {
355
+ const provider = new ProductVariableProvider_1.ProductVariableProvider(packages, config.trialEligibility);
356
+ Object.assign(bag, provider.getVariablesForProduct(selectedIndex)); // product.* (+ user.isEligibleForIntro/hasUsedTrial)
357
+ Object.assign(bag, provider.getSelectedProductVariables(selectedIndex)); // selectedProduct.*
358
+ Object.assign(bag, provider.getAllProductVariables()); // products.N.*
359
+ Object.assign(bag, provider.getConvenienceVariables()); // primary.*/secondary.*/…
360
+ bag['packages.count'] = packages.length;
361
+ bag['products.selectedIndex'] = selectedIndex;
362
+ }
363
+ // user.seed — deterministic A/B bucket (canonical murmur; parity with native).
364
+ if (opts === null || opts === void 0 ? void 0 : opts.appUserId)
365
+ bag['user.seed'] = (0, murmur_1.userSeedBucket)(opts.appUserId);
366
+ // user.subscription — active entitlement tier, from the SYNC-cached CustomerInfo
367
+ // (may be null on cold start; best-effort, same async limitation as iOS Phase 2).
368
+ // Sorted for cross-platform determinism with iOS/Android.
369
+ const customer = opts === null || opts === void 0 ? void 0 : opts.customerInfo;
370
+ if (customer) {
371
+ const active = ((_b = customer.entitlements) !== null && _b !== void 0 ? _b : []).filter((e) => e.isActive).map((e) => e.identifier).sort();
372
+ bag['user.subscription'] = (_c = active[0]) !== null && _c !== void 0 ? _c : '';
373
+ bag['user.hasActiveSubscription'] = ((_e = (_d = customer.activeSubscriptions) === null || _d === void 0 ? void 0 : _d.length) !== null && _e !== void 0 ? _e : 0) > 0 || active.length > 0;
374
+ bag['user.activeEntitlements'] = active.join(',');
375
+ }
376
+ return bag;
377
+ }
378
+ }
379
+ exports.VariableResolver = VariableResolver;
@@ -0,0 +1,75 @@
1
+ /**
2
+ * WebViewPaywall — Optional WebView-based paywall component for the
3
+ * React Native SDK.
4
+ *
5
+ * The default React Native paywall path is native (`PaywallView` →
6
+ * `ComponentRenderer`). This component is the WebView alternative that
7
+ * loads the same unified renderer bundle iOS / Android / Flutter use,
8
+ * so paywalls visually match across all platforms at the cost of one
9
+ * extra `react-native-webview` dependency.
10
+ *
11
+ * Android parity reference:
12
+ * - `BillDogWebViewPaywall` (Android, billdog-paywalls-webview module)
13
+ * - Canonical message protocol:
14
+ * packages/paywall-renderer/src/native/messageProtocol.ts
15
+ * - postMessage events handled here mirror the protocol's
16
+ * OutboundMessageType union: purchase / restore / close / open_url /
17
+ * track / state_changed / error / ready.
18
+ *
19
+ * Architecture choice: URL-based loading (same as Flutter) rather than
20
+ * bundled HTML asset. The renderer is served by the `render-paywall`
21
+ * edge function at `https://api.billdog.io/v1/render-paywall?...`,
22
+ * and the paywall config is delivered to the renderer via the JS
23
+ * bridge function `window.loadPaywallData(...)` — same contract as
24
+ * the Flutter SDK uses (packages/flutter/lib/src/widgets/paywall_view.dart).
25
+ *
26
+ * Bundling the HTML as a static asset requires a metro.config.js change
27
+ * (`assetExts: ['html', ...]` + `metro-html-loader`). URL-based avoids
28
+ * that and is the simpler MVP. If first-paint latency becomes a problem,
29
+ * a future revision can switch to local-asset loading.
30
+ */
31
+ import React from 'react';
32
+ export interface WebViewPaywallProps {
33
+ /** Paywall identifier — matches the placement id used elsewhere. */
34
+ paywallIdentifier: string;
35
+ /** API key for the BillDog project. */
36
+ apiKey: string;
37
+ /** Optional current user id; forwarded to the renderer so it can
38
+ * resolve per-user variants / experiments. */
39
+ customerId?: string;
40
+ /** Render the WebView inside a fullscreen modal. Default true. */
41
+ modal?: boolean;
42
+ /** Visibility (only meaningful when `modal=true`). */
43
+ visible?: boolean;
44
+ /**
45
+ * Explicit locale for the rendered paywall. When omitted the component
46
+ * resolves the effective locale via `LocalizationResolver.getEffectiveLocale()`
47
+ * (runtime override > device locale > "en") — matching the Android
48
+ * `WebViewPaywallActivity.effectiveLocale()` precedence.
49
+ */
50
+ locale?: string;
51
+ /** Fires when the renderer reports `ready` over the postMessage bridge. */
52
+ onReady?: () => void;
53
+ /** Fires when the consumer should initiate a purchase. The product id
54
+ * comes from the renderer's `purchase` message. */
55
+ onPurchase?: (productIdentifier: string) => void;
56
+ /** Fires when the user taps Restore. */
57
+ onRestore?: () => void;
58
+ /** Fires when the user closes the paywall. */
59
+ onDismiss?: () => void;
60
+ /** Fires on `open_url` messages (e.g. terms / privacy links). */
61
+ onOpenUrl?: (url: string) => void;
62
+ /** Fires on `track` analytics events. */
63
+ onTrackEvent?: (eventName: string, properties?: Record<string, unknown>) => void;
64
+ /** Fires on any error message from the renderer. */
65
+ onError?: (error: Error) => void;
66
+ /**
67
+ * Optional custom action execution handler (Route B).
68
+ * If provided, the consumer must execute the action and return a promise
69
+ * resolving with the result or rejecting with an error. If omitted, the
70
+ * SDK automatically executes it via the server-side Edge function.
71
+ */
72
+ onExecuteAction?: (paywallId: string | null, componentId: string, actionType: string, idempotencyKey: string | null) => Promise<unknown>;
73
+ }
74
+ export declare const WebViewPaywall: React.FC<WebViewPaywallProps>;
75
+ export default WebViewPaywall;