npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nsupp/react-native-sdk

v0.1.0

Published

Expo Module bridge for the nsupp chat iOS and Android SDKs — native chat screen, identity, push.

Readme

@nsupp/react-native-sdk

nsupp destek sohbetini React Native / Expo uygulamanıza gömer. Sohbet arayüzü web widget'ının kendisidir: görünüm ve işlevin tamamı çalışma alanı ayarından, tek noktadan gelir.


Mimari: köprü + web widget'ının kendisi

Bu paket ne protokolü ne de arayüzü yeniden yazar. packages/ios-sdk (Swift) ve packages/android-sdk (Kotlin) paketlerini saran ince bir Expo Module köprüsüdür; o paketler de sohbeti bir WebView'da aynı widget.js ile gösterir — web sitenizdeki widget'ın ta kendisi.

Sonuç: ön-sohbet formu, Makaleler sekmesi, hamburger menüsü, powered-by, CSAT, kesinti bandı, hızlı yanıtlar ve dosya eki mobilde de birebir aynıdır, çünkü tek bir yerde yazılıdır. Panelde ayarı değiştirdiğinizde web ve mobil AYNI ANDA değişir.

Neden: saf-JS bir uygulama protokolün dördüncü kopyası olurdu (widget, iOS, Android + bu). Yakın zamanda yapılan denetim tek bir kopyada bile dört ayrı hata buldu — yoklama imlecinin kayması, open/mobile bayraklarının hiç gitmemesi, oturum jetonunun sorgu dizesine sızması ve "yeni konu" sinyalinin hiçbir yere ulaşmaması. Bunları dördüncü kez yazmak, dördüncü kez üretmek demekti.

Sektör emsali de aynı yönde: bu alandaki yerleşik canlı-destek ürünlerinin resmi React Native paketleri de yerel SDK'ları saran Expo Module köprüleridir, JS yeniden uygulamaları değil.

Bedeli açıkça

Yerel kod içerdiği için Expo Go ile çalışmaz. expo prebuild + development build (ya da bare workflow) gerekir. Yerel kod taşıyan her kütüphane için durum aynıdır.


Kurulum

npm install @nsupp/react-native-sdk
npx expo prebuild

ios/ tarafında CocoaPods otomatik bağlanır (expo-module.config.json); ek bir adım yok.

Yayın durumu: paket npm'e henüz yayınlanmadı (yayın anahtarları gerekiyor). Şu an depo içinden yol ile bağlanır. API yüzeyi her iki durumda da aynıdır — yalnız bağımlılık satırı değişecek.

Kullanım

import Nsupp from '@nsupp/react-native-sdk';

// Uygulamanın en üstünde, BİR KEZ:
Nsupp.configure('https://api.nsupp.com', 'pk_…');

pk_… = panelde Settings → Setup & Integrations ekranındaki gömme kodunda geçen data-public-key. Gizli değildir; tarayıcıda da açıkta durur. Çalışma alanında kayıtlı bir uygulama varsa üçüncü argüman olarak uygulama anahtarı da gerekir (aşağıda).

Üç yol — hangisi olacağına siz karar verirsiniz

Üçü de aynı yüzeydir; nasıl göründüğü uygulamanızın kararıdır. Hiçbiri dayatılmaz.

Kip adları mobilde balon ve ekran. Balon-olmayan kipin adı mobilde (iOS · Android · RN) ekrandır — tam ekran açılır. Masaüstünde (macOS · Electron) aynı yerde panel vardır ve o, köşede kayan bir penceredir. Ad platform sınıfına göre değişir çünkü davranış gerçekten farklıdır: telefonda "köşede kayan pencere" diye bir kip yok ve olmayacak, ekran buna elverişli değil. Aynı adı iki davranışa vermek yalan söylemek olurdu. balon beş platformda da aynı anlamdadır.

① Köşede ikon (balon / launcher) — web'deki balonun karşılığı: ikon köşede durur, dokununca sohbet yüzeyi açılır/kapanır.

Nsupp.configure('https://api.nsupp.com', 'pk_…');
Nsupp.showLauncher();   // ekran açıkken, openChat()'ten ÖNCE

İkon şeffaf tam-ekran bir katman değildir: yalnız kapladığı daire dokunma yakalar, geri kalan her yer sizin arayüzünüzdür ve tıklanabilir kalır. Panel açıkken de uygulamanız görünür durur.

② Sayfaya gömülü — sohbet kendi ekranınızın içinde yaşar (yan panel, sekme, "Destek" sayfası, bölünmüş görünümün yarısı):

import { NsuppChatView } from '@nsupp/react-native-sdk';

<NsuppChatView style={{ flex: 1 }} />

③ Kendi düğmeniz + tam ekran (ekran kipi) — köşede hiçbir şey durmaz:

<Button title="Destek" onPress={() => Nsupp.openChat()} />

Bilinmesi gerekenler:

  • Kip bir kez seçilir. showLauncher()ı configure()dan sonra, sohbet daha açılmadan çağırın: kip, yerel sunum kurulurken belirlenir. Sohbet AÇIKKEN çağırmak desteklenmiyor — ikonun o anda açık olan pencereyle ilişkisi platforma göre değişir, tek bir davranış söz veremeyiz. showLauncher() çağrıldıysa openChat() de balonun kendi yüzeyini açar, ayrı bir tam ekran değil — aksi hâlde tek çağrı, uygulamanın kipine göre iki farklı yüzey gösterirdi.
  • Gömülü görünümün boyutunu siz verirsiniz. Yerel görünümün doğal boyutu yoktur; flex: 1 ya da açık bir yükseklik verin, yoksa 0 yükseklikte (görünmez) çizilir.
  • Görünüm panelden gelir. <NsuppChatView />in renk/metin prop'u YOKTUR — olsaydı kabuk çalışma alanı ayarını geçersiz kılar ve platformlar birbirinden ayrışırdı.
  • Aynı anda tek yüzey. Gömülü görünümü iki yerde göstermeyin ve gömülüyken sohbeti ayrıca açmayın. Kurallar:
    • reset() (çıkış) her yüzeye iner — hem gömülü görünüme hem hazır sunuma. Bu bir güvenlik sözüdür, "en son kurulan yüzey"e bırakılamaz: açık kalan bir panel, çıkış yapmış kullanıcının sohbetini göstermeye devam ederdi.
    • Bildirimden konuşma açma (openChat(id)) en son kurulan yüzeye gider, öncekine gitmez.
    • Gömülü görünüm ile hazır sunum aynı anda canlıysa ikisi ayrı ziyaretçi oturumu açabilir (bölünmüş kimlik: aynı kişi sunucuda iki ziyaretçi). Yerel taraf bunu cihaz günlüğüne uyarı olarak yazar; sessizce bırakmıyoruz çünkü teşhis edilemeyen hata en pahalısıdır.
  • Derin bağlantı openChat(conversationId) ile verilir — gömülü görünümde de bu çağrı kullanılır. Modül düzeyindeki openConversation() yerel oturumun imlecini oynatır, ekrandaki yüzeyi değiştirmez.
  • Gömülü görünüm yüklenemezse (ör. configure() çağrılmadan yerleştirildiyse) sebebi cihaz günlüğüne yazar (logcat / Xcode konsolu). Bunu JS'e olay olarak taşımak için çekirdek SDK'ların gömülü görünümlerinde bir hata geri-çağrımı gerekiyor; şu an yok, bu yüzden söz de verilmiyor.

Kullanıcıyı tanıtma

// Kullanıcı GİRİŞ YAPTIĞI ANDA çağırın — sohbet ekranını beklemeyin.
Nsupp.identify('[email protected]', {
  name: 'Ada Lovelace',
  signature: imzaSunucudanGeldi,      // HMAC-SHA256(email, identity_secret)
  attributes: { plan: 'pro', segments: ['vip'] },
});

Oturum henüz yoksa kimlik yerel tarafta bekletilir ve sohbet ilk açıldığında gönderilir. Kuyruk olmasaydı çağrı sessizce düşer, müşteri operatörde anonim görünür ve VIP/segment yönlendirmesi hiç çalışmazdı.

signature sunucunuzda üretilir. Uygulamaya gömülen bir sır doğrulamayı anlamsız kılar; JS paketine gömülen sır ise ayrıca .js bundle'ından okunabilir.

Nsupp.setSessionData({ sonSiparis: '#1042' });  // önce identify gerekir
Nsupp.setSegments(['vip']);                      // attributes.segments'i DEĞİŞTİRİR
Nsupp.trackEvent('Checkout');
Nsupp.runTrigger('hosgeldin');                   // mesaj tetikleyicisi / bot senaryosu
Nsupp.rate(5, 'çok hızlıydı');                   // CSAT
Nsupp.startNewConversation();                    // ayrı konu; öncekiler KAPANMAZ
Nsupp.openConversation(id);                      // oturum imleci; EKRANDAKİ yüzeyi değiştirmez
Nsupp.reset();                                   // ÇIKIŞTA çağırın

// Yardım merkezi (self-servis — sohbeti hiç açmadan çözülen sorular)
const makaleler = await Nsupp.loadArticles();
const sonuc = await Nsupp.searchArticles('kargo');
const makale = await Nsupp.getArticle('iade');

Anlık bildirim

Panelde Settings → Push Notifications altına FCM/APNS kimlik bilgilerini girin (Mobile Push Notifications), sonra cihaz jetonunu bildirin:

// iOS: APNS jetonunun HEX metni · Android: FCM jetonu
Nsupp.registerPushToken(jeton);

Bildirime dokunulduğunda ilgili konuşmayı açmak için:

Nsupp.openChat(bildirimYuku.conversationId);

Bildirimin gösterimi uygulamanın işidir (kanal, ikon, ses, Android 13+ izin akışı). SDK bunları dayatmaz — dayatsaydı uygulamanızın bildirim düzenini bozardı.


Gizlilik bildirimi (PrivacyInfo.xcprivacy)

iOS tarafında pod kendi bildirimini TAŞIRios/PrivacyInfo.xcprivacy, podspec'te resource_bundles ile bildirilir (NsuppSdkPrivacy.bundle). Beyan ettiği tek şey NSPrivacyAccessedAPICategoryUserDefaults / CA92.1: ziyaretçi jetonunu UserDefaultsta saklarız. İzleme yoktur (NSPrivacyTracking = false).

Bu olmasaydı sizin yüklemeniz ITMS-91053: Missing API declaration ile reddedilirdi.

Size düşen — toplanan veri türleri: e-posta/ad yalnız identify() çağırırsanız, destek mesajları ve ekleri ziyaretçi gönderdiğinde, ziyaretçi jetonu her oturumda gider. Bunları kendi App Store Connect gizlilik anketinizde ve Google Play Data safety formunuzda siz bildirirsiniz; SDK sizin adınıza beyan edemez, çünkü ne göndereceğiniz sizin kullanımınıza bağlıdır.

Bilinmesi gerekenler

Çıkışta reset() çağırın. Ziyaretçi jetonu kimliğe değil cihaza bağlıdır; çağırmazsanız paylaşılan bir cihazda sonraki kullanıcı öncekinin sohbet geçmişini açar.

setSessionData önce identify ister. Öznitelikler CRM'deki kişi kaydında yaşar ve kişi e-posta ile doğar. Kimlik verilmemişse çağrı sessizce yutulmaz — yerel taraf hata durumuna yazar.

setSegments değiştirir, birleştirmez. İstemci mevcut segmentleri bilmez; birleştirme sözü verseydik yalan olurdu.

Sohbet WebView'ı kendi origin'inde kalır. configure()a verdiğiniz adres bir origin olmalıdır (https://api.nsupp.com, yol taşımadan): kabuk uygulama anahtarını ve ziyaretçi jetonunu yalnız o origin'deki belgeye verir, başka bir adrese giden gezinme — bağlantı, JS yönlendirmesi, form ya da sunucu 302'si — WebView'a yüklenmez. Ayrıştırılamayan bir adres verilirse sohbet görünür şekilde açılmaz (sessizce her adrese izin vermek yerine).

Dış bağlantılarda şema süzgeci var. Sohbet/makale içeriğinden yalnız http, https, mailto ve tel sistem tarayıcısına/uygulamasına devredilir; kendi myapp:// şemanızı sohbete koyarsanız açılmaz (uzak içerik cihazdaki uygulamaların derin bağlantılarını tetikleyemesin diye).

Çalışma alanında kayıtlı bir uygulama varsa appKey ZORUNLUdur. Ölçüt alan adı kilidi değil, KAYITtır. Alan adı kilidi bir tarayıcı kontrolüdür: web widget'ı Origin başlığı gönderir, yerel uygulama göndermez — bu yüzden yerel yüzeyin kendi kimliği vardır. O kimlik bir uygulama anahtarıdır: panelde Ayarlar → Uygulamalar'dan üretilir, bir kez gösterilir ve configure()ın üçüncü argümanı olarak verilir:

Nsupp.configure('https://api.nsupp.com', 'pk_…', 'nsupp_app_…');
  • Panelden bir uygulama kaydettiğiniz andan itibaren, o çalışma alanına gelen anahtarsız yerel istek 403 domain_locked alır — alan adı kilidi kapalı olsa bile. Hiç kaydı olmayan çalışma alanları etkilenmez (geriye-uyum).
  • Kilit açık olması tek başına yetmez: kilidin dayanacağı bir alan adı yoksa (yalnız mobil uygulaması olan çalışma alanı) kilit hiç uygulanmaz ve anahtar da aranmaz. Kapının kayda bağlanmasının sebebi tam olarak budur — kilide bağlansaydı bu SDK'nın hedef müşterisi, anahtar üretip uygulamasına koyduktan sonra bile korumasız kalırdı.
  • Kilit açık ve alan adı tanımlıysa yerel istek ayrıca o kilide de takılır (WebView'ın kendi origin'i izinli listede değildir), yani anahtar orada da gerekir.
  • Anahtar sızarsa panelden döndürün; eskisi anında geçersizleşir.
  • Eskiden muafiyetin dayanağı x-nsupp-sdk-platform başlığıydı — o bir beyandı, curl da aynı başlığı yazabiliyordu. Artık dayanak doğrulanabilir: sunucu anahtarı kaydına çözer ve kaydın O çalışma alanına ait olmasını arar.
  • Dürüst sınır: anahtar uygulama paketinden çıkarılabilir (IPA/APK incelenebilir). Kişi-düzeyi güvence identify imzasının işidir; bu anahtar onun yerine geçmez.

Geliştirme

npm run test:rn-sdk

24 test koşar. Bu depoda Expo ve Android SDK'sı yok, yani ios/NsuppSdkModule.swift ile android/.../NsuppSdkModule.kt burada derlenmez. Testler o boşluğun kapağıdır:

  • TypeScript arayüzü ile Swift ve Kotlin köprülerinin fonksiyon adları birebir aynı mı
  • Köprünün çağırdığı her yerel sembol (Nsupp.x(), session.x()) çekirdekte gerçekten var mı
  • İsteğe bağlı argümanlar undefined değil null olarak geçiyor mu (Expo köprüsü undefinedı opsiyonel argüman saymaz; sayılmazsa çağrı sessizce düşer)
  • Gömülü görünüm sözleşmesi: istenen görünüm yöneticisinin adı = modül adı, iki yerel taraf da AYNI ExpoView sınıfını açıyor, TS'e eklenen her prop'un karşılığında yerel Prop("…") var. (Görünüm sözleşmesi fonksiyonlardan daha sessiz kırılır: yanlış ad ya da eksik Prop hata bile vermez — bileşen boş çizilir, prop yok sayılır.)
  • Köşe ikonu ve gömülü görünüm çekirdeğin GERÇEK yüzeylerini kullanıyor mu (NsuppWebChatView, NsuppChatPresenter(kip)) — kabuk kendi WebView'ını/balonunu kurarsa test kırılır
  • reset() her yüzeye iniyor mu (oturum + gömülü görünüm + hazır sunum) ve gömülü görünüm ikinci bir denetleyici kurmuyor mu — bu testler YORUM-KÖRdür (kablolama sökülüp yalnız gerekçe yorumu kalırsa yeşil kalmazlar) ve iOS'un hizalandığı Android davranışını da ayrıca doğrularlar
  • README'deki uygulama-anahtarı iddiası gerçeğe uygun mu. Bu iddia İKİ KEZ yanlış yazıldı: önce "kilit mobili etkilemez, başlıkla geçilir", sonra "kilit açıksa anahtar zorunlu". İkincisi de yanlıştı ve ölçümle çürütüldü — kapı kilide değil KAYDA bağlı (originAllowed, apps/server/src/widget-routes.ts). Test artık doğru ölçütü pinler; yanlış ölçütü yazan bir README'yi kırar

Derleyici yerine geçmezler (tip/imza denetimi yapmazlar) ama köprülerde en sık kırılan şeyi — ad ayrışmasını — yakalarlar. Nitekim bu testler yazılırken gerçek bir hata bulundu: köprü NsuppChatView(session:) çağırıyordu, o başlatıcı yoktu.