signalbird 2.5.0 → 2.6.0

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 (51) hide show
  1. package/README.md +37 -33
  2. package/dist/angular.d.mts +4 -4
  3. package/dist/angular.d.ts +4 -4
  4. package/dist/angular.js +56 -13
  5. package/dist/angular.js.map +1 -1
  6. package/dist/angular.mjs +56 -13
  7. package/dist/angular.mjs.map +1 -1
  8. package/dist/app.d.mts +37 -47
  9. package/dist/app.d.ts +37 -47
  10. package/dist/app.js +55 -12
  11. package/dist/app.js.map +1 -1
  12. package/dist/app.mjs +55 -12
  13. package/dist/app.mjs.map +1 -1
  14. package/dist/browser.d.mts +1 -13
  15. package/dist/browser.d.ts +1 -13
  16. package/dist/browser.js +25 -3
  17. package/dist/browser.js.map +1 -1
  18. package/dist/browser.mjs +25 -3
  19. package/dist/browser.mjs.map +1 -1
  20. package/dist/embed.d.mts +6 -6
  21. package/dist/embed.d.ts +6 -6
  22. package/dist/embed.js +1 -1
  23. package/dist/embed.js.map +1 -1
  24. package/dist/embed.mjs +1 -1
  25. package/dist/embed.mjs.map +1 -1
  26. package/dist/index.d.mts +50 -50
  27. package/dist/index.d.ts +50 -50
  28. package/dist/index.js +48 -22
  29. package/dist/index.js.map +1 -1
  30. package/dist/index.mjs +48 -22
  31. package/dist/index.mjs.map +1 -1
  32. package/dist/react-native.d.mts +2 -2
  33. package/dist/react-native.d.ts +2 -2
  34. package/dist/react-native.js +55 -12
  35. package/dist/react-native.js.map +1 -1
  36. package/dist/react-native.mjs +55 -12
  37. package/dist/react-native.mjs.map +1 -1
  38. package/dist/react.d.mts +4 -4
  39. package/dist/react.d.ts +4 -4
  40. package/dist/react.js +55 -12
  41. package/dist/react.js.map +1 -1
  42. package/dist/react.mjs +55 -12
  43. package/dist/react.mjs.map +1 -1
  44. package/dist/signalbird.js +20 -20
  45. package/dist/vue.d.mts +3 -3
  46. package/dist/vue.d.ts +3 -3
  47. package/dist/vue.js +55 -12
  48. package/dist/vue.js.map +1 -1
  49. package/dist/vue.mjs +55 -12
  50. package/dist/vue.mjs.map +1 -1
  51. package/package.json +2 -2
package/dist/app.d.mts CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Bu yüzey MÜŞTERİNİN MÜŞTERİSİ içindir: ziyaretçi ya da uygulama kullanıcısı.
5
5
  * Anahtarı açıktır (`sb_public_live_…`) ve istemciye gömülür; güvenliği gizlilikten
6
- * değil kısıttan gelir — yalnız izinli kökenden çalışır ve yalnız ziyaretçinin
6
+ * değil kısıttan gelir - yalnız izinli kökenden çalışır ve yalnız ziyaretçinin
7
7
  * KENDİ verisine dokunur.
8
8
  */
9
9
  interface SbResult<T = unknown> {
@@ -33,21 +33,21 @@ interface AppConfig {
33
33
  */
34
34
  platform?: 'ios' | 'android' | 'web';
35
35
  /**
36
- * Açık domain anahtarı (`sb_public_live_…`) — KİMLİĞİ doğrular.
36
+ * Açık domain anahtarı (`sb_public_live_…`) - KİMLİĞİ doğrular.
37
37
  *
38
38
  * Panel → Alan adları → [alan adı] → Anahtarlar. Web anahtarı yalnız izinli
39
39
  * kökenlerden, mobil anahtarı yalnız Origin taşımayan isteklerden çalışır.
40
40
  */
41
41
  publicKey: string;
42
42
  /**
43
- * Sohbet kanalı — `chat` modülünün anahtarı (`destek`). DAVRANIŞI seçer:
43
+ * Sohbet kanalı - `chat` modülünün anahtarı (`destek`). DAVRANIŞI seçer:
44
44
  * hangi widget ayarı, hangi gelen kutusu.
45
45
  *
46
46
  * Anahtardan AYRIDIR ve bu bilinçlidir (1 Eyl 2026): domain anahtarını
47
47
  * yenilediğinizde kanal adı değişmez, yani kodunuz aynı kalır.
48
48
  */
49
49
  chatKey?: string;
50
- /** Push kanalı — `push` modülünün anahtarı. Cihaz kaydı için gerekir. */
50
+ /** Push kanalı - `push` modülünün anahtarı. Cihaz kaydı için gerekir. */
51
51
  pushKey?: string;
52
52
  /** Varsayılan: https://live.signalbird.io/api */
53
53
  baseUrl?: string;
@@ -73,7 +73,7 @@ interface TopicOption {
73
73
  description?: string | null;
74
74
  parent_id?: number | null;
75
75
  }
76
- /** `POST /v1/sdk/bootstrap` yanıtı — widget çizilmeden önceki tek soru. */
76
+ /** `POST /v1/sdk/bootstrap` yanıtı - widget çizilmeden önceki tek soru. */
77
77
  interface BootstrapResult {
78
78
  /**
79
79
  * Sözleşme adı `channel` (1 Eyl 2026); `app` eski sunucular için okunur.
@@ -85,10 +85,10 @@ interface BootstrapResult {
85
85
  app?: BootstrapChannel;
86
86
  /** Boşsa konu adımı hiç gösterilmez. */
87
87
  topics?: TopicOption[];
88
- /** Mevcut açık konuşma (varsa) — ChatSession ilk listelemeyi atlar. */
88
+ /** Mevcut açık konuşma (varsa) - ChatSession ilk listelemeyi atlar. */
89
89
  conversation?: Conversation | null;
90
90
  /**
91
- * Canlı bağlantı — YALNIZ ADRES. Anahtar ya da sır taşımaz: bağlanan taraf
91
+ * Canlı bağlantı - YALNIZ ADRES. Anahtar ya da sır taşımaz: bağlanan taraf
92
92
  * hiçbir şey göremez, odaya girmek imza ister ve imzayı
93
93
  * `POST /v1/sdk/chat/socket/auth` verir.
94
94
  *
@@ -139,7 +139,7 @@ interface BootstrapChannel {
139
139
  }
140
140
  interface Visitor {
141
141
  id: string;
142
- /** Ziyaretçi sırrı — YALNIZ oturum açılışında döner, sonra saklanır. */
142
+ /** Ziyaretçi sırrı - YALNIZ oturum açılışında döner, sonra saklanır. */
143
143
  secret?: string;
144
144
  name?: string | null;
145
145
  email?: string | null;
@@ -196,7 +196,7 @@ interface Message {
196
196
  edited_at?: string | null;
197
197
  created_at?: string;
198
198
  /**
199
- * Anlık çeviri — ziyaretçinin diline. Arayüz varsa bunu gösterir; orijinal
199
+ * Anlık çeviri - ziyaretçinin diline. Arayüz varsa bunu gösterir; orijinal
200
200
  * `body`'de durmaya devam eder.
201
201
  */
202
202
  translation?: {
@@ -211,7 +211,7 @@ interface Message {
211
211
  *
212
212
  * Tip listede yoktu ama oturum bu alanı yazıyordu: arayüz "gitmedi" hâlini
213
213
  * çizmek istediğinde tipi zorlamak (`as`) zorunda kalıyordu. Bekleyen mesaj
214
- * ayrıca işaretlenmez — sunucu yanıtı gelene kadar `id` ile `client_id`
214
+ * ayrıca işaretlenmez - sunucu yanıtı gelene kadar `id` ile `client_id`
215
215
  * aynıdır ve bu, "henüz yolda" demenin en ucuz yoludur.
216
216
  */
217
217
  failed?: boolean;
@@ -240,7 +240,7 @@ interface StartConversationInput {
240
240
  attachments?: unknown[];
241
241
  page_url?: string;
242
242
  /**
243
- * Destek konusu — id ya da slug. Seçim SUNUCUDA doğrulanır; geçersizse
243
+ * Destek konusu - id ya da slug. Seçim SUNUCUDA doğrulanır; geçersizse
244
244
  * konuşma yine açılır, konu yok sayılır.
245
245
  */
246
246
  topic?: string | number;
@@ -252,7 +252,7 @@ interface SendMessageInput {
252
252
  attachments?: unknown[];
253
253
  }
254
254
  interface ConversationQuery {
255
- /** `cm_…` imleci — yalnız bundan sonrakiler döner. */
255
+ /** `cm_…` imleci - yalnız bundan sonrakiler döner. */
256
256
  after?: string;
257
257
  limit?: number;
258
258
  }
@@ -267,26 +267,6 @@ interface RegisterDeviceInput {
267
267
  locale?: string;
268
268
  }
269
269
 
270
- /**
271
- * Uygulama istemcisi — son kullanıcı tarafı (sohbet + push kaydı).
272
- *
273
- * Tek bir sınıf; tarayıcı, React Native, Electron ve test aynı gövdeyi kullanır.
274
- * Platform farkı iki noktada toplanmıştır ve ikisi de dışarıdan verilir:
275
- * `storage` (ziyaretçi sırrı nerede durur) ve `fetchImpl`. Çatıya özel sarmalayıcı
276
- * yazmak yerine bunu seçtik — React, Vue, Angular ve RN uyarlamaları bu sınıfın
277
- * ÜSTÜNE oturur, kopyası değildir.
278
- *
279
- * Kimlik iki parçadır: açık domain anahtarı (`X-Signalbird-Key`) ve
280
- * ziyaretçi sırrı (`X-Signalbird-Visitor`). Sır yalnız oturum açılışında döner;
281
- * kaybolursa yeni oturum açılır ve geçmiş konuşmalar görünmez — bu yüzden
282
- * saklama katmanı zorunludur, isteğe bağlı değil.
283
- *
284
- * Hiçbir metot istisna fırlatmaz: sohbet balonunun hatası müşterinin ödeme
285
- * sayfasını çökertmemeli. Sonuç her zaman `{ok, status, …}` zarfıdır.
286
- *
287
- * Sözleşme: docs/CONTRACT.md § 11
288
- */
289
-
290
270
  /** RFC 4122 uyumlu olmak zorunda değil; tek işi yerel kopyayı eşlemek. */
291
271
  declare function clientId(): string;
292
272
  declare class SignalbirdApp {
@@ -324,7 +304,7 @@ declare class SignalbirdApp {
324
304
  identify(input: IdentifyInput): Promise<SbResult<{
325
305
  visitor: Visitor;
326
306
  }>>;
327
- /** Saklanan ziyaretçi kimliği — yoksa `null`. */
307
+ /** Saklanan ziyaretçi kimliği - yoksa `null`. */
328
308
  currentVisitor(): Promise<{
329
309
  id: string;
330
310
  name?: string | null;
@@ -343,7 +323,7 @@ declare class SignalbirdApp {
343
323
  within_hours?: boolean;
344
324
  }>>;
345
325
  /**
346
- * İlk mesajla konuşma açar. Kota burada harcanır — konuşma başına sayılır,
326
+ * İlk mesajla konuşma açar. Kota burada harcanır - konuşma başına sayılır,
347
327
  * mesaj başına değil.
348
328
  */
349
329
  startConversation(input: StartConversationInput): Promise<SbResult<{
@@ -378,21 +358,21 @@ declare class SignalbirdApp {
378
358
  rateConversation(conversationId: string, rating: number, comment?: string): Promise<SbResult<unknown>>;
379
359
  /**
380
360
  * Cihaz token'ını kaydeder. Token'ı almak (FCM/APNs/Web Push izni) ev
381
- * sahibinin işidir; SDK yalnız iletir — izin diyaloğunu kimin, ne zaman
361
+ * sahibinin işidir; SDK yalnız iletir - izin diyaloğunu kimin, ne zaman
382
362
  * göstereceği ürün kararıdır, kütüphane kararı değil.
383
363
  */
384
364
  registerDevice(input: RegisterDeviceInput): Promise<SbResult<unknown>>;
385
365
  /** Çıkışta çağrılır: kayıt silinmez, kapatılır (geçmiş korunur). */
386
366
  unregisterDevice(token: string): Promise<SbResult<unknown>>;
387
367
  /**
388
- * Bildirime dokunuldu — açılma damgası.
368
+ * Bildirime dokunuldu - açılma damgası.
389
369
  *
390
370
  * Push'ta açılmayı YALNIZCA uygulama bilir: FCM/APNs "teslim ettim" der,
391
371
  * "kullanıcı dokundu" demez. Bildirim yükündeki `data.sb_message_id`
392
372
  * değerini buraya geri gönderin.
393
373
  *
394
374
  * ```ts
395
- * // React Native / Expo — bildirime dokunma işleyicisinde
375
+ * // React Native / Expo - bildirime dokunma işleyicisinde
396
376
  * const id = response.notification.request.content.data?.sb_message_id
397
377
  * if (id) await sb.reportPushOpened(String(id))
398
378
  * ```
@@ -407,12 +387,12 @@ declare class SignalbirdApp {
407
387
  }
408
388
 
409
389
  /**
410
- * Sohbet oturumu — çatısız durum yönetimi.
390
+ * Sohbet oturumu - çatısız durum yönetimi.
411
391
  *
412
392
  * `SignalbirdApp` ham uçları verir; burası bir sohbet ekranının gerçekten
413
393
  * ihtiyaç duyduğu şeyi verir: mesaj listesi, okunmamış sayısı, yazıyor durumu,
414
394
  * iyimser gönderim ve yoklama merdiveni. React/Vue/Angular/React Native
415
- * uyarlamaları bu sınıfa abone olur — üçünde de aynı mantığı yeniden yazmak,
395
+ * uyarlamaları bu sınıfa abone olur - üçünde de aynı mantığı yeniden yazmak,
416
396
  * üç ayrı hata takımı üretmek demekti.
417
397
  *
418
398
  * ── CANLI BAĞLANTI + YOKLAMA ──────────────────────────────────────────────
@@ -450,9 +430,9 @@ interface ChatState {
450
430
  * Seçimi `setTopic()` taşır; ilk konuşma açılırken gönderilir.
451
431
  */
452
432
  topics: TopicOption[];
453
- /** Seçili konu (slug) — ilk konuşmayla birlikte gider. */
433
+ /** Seçili konu (slug) - ilk konuşmayla birlikte gider. */
454
434
  topic: string | null;
455
- /** Son hatanın kodu — arayüz isterse gösterir, göstermezse yutar. */
435
+ /** Son hatanın kodu - arayüz isterse gösterir, göstermezse yutar. */
456
436
  errorCode?: string;
457
437
  /**
458
438
  * Uygulamanın sohbet ayarları (renk, logo, tema, puanlama bağlantısı…).
@@ -464,7 +444,7 @@ interface ChatState {
464
444
  }
465
445
  type ChatListener = (state: ChatState) => void;
466
446
  interface ChatSessionOptions {
467
- /** Panel açık mı — yoklama hızını belirler. */
447
+ /** Panel açık mı - yoklama hızını belirler. */
468
448
  active?: boolean;
469
449
  /** Arka plandayken tur atlanır; varsayılan: `document.visibilityState`. */
470
450
  isVisible?: () => boolean;
@@ -491,22 +471,24 @@ declare class ChatSession {
491
471
  * da düzenleme ekrana hiç yansımaz.
492
472
  */
493
473
  private forceFull;
474
+ /** Son gönderilen `typing(true)` zamanı; 0 = açık sinyal yok. */
475
+ private lastTypingSent;
494
476
  constructor(app: SignalbirdApp, options?: ChatSessionOptions);
495
477
  subscribe(listener: ChatListener): () => void;
496
478
  snapshot(): ChatState;
497
479
  /** Bootstrap + varsa mevcut konuşmayı yükler, sonra yoklamayı başlatır. */
498
480
  start(): Promise<void>;
499
- /** Panel açıldı/kapandı — yoklama hızı buna göre değişir. */
481
+ /** Panel açıldı/kapandı - yoklama hızı buna göre değişir. */
500
482
  setActive(active: boolean): void;
501
483
  /**
502
484
  * Konuşmayı bırakır; sonraki mesaj YENİ bir konuşma açar.
503
485
  *
504
486
  * Ekranın "yeni sohbet" düğmesi de bunu çağırır. Sunucuda hiçbir şey
505
- * silinmez — yalnız bu oturumun neye baktığı değişir.
487
+ * silinmez - yalnız bu oturumun neye baktığı değişir.
506
488
  */
507
489
  reset(): void;
508
490
  stop(): void;
509
- /** Canlı bağlantı kurulu mu — arayüz isterse gösterir (zorunlu değil). */
491
+ /** Canlı bağlantı kurulu mu - arayüz isterse gösterir (zorunlu değil). */
510
492
  get isLive(): boolean;
511
493
  /** Ön-form gönderildiğinde ya da uygulama kullanıcıyı tanıdığında. */
512
494
  openSession(input: SessionInput): Promise<SbResult<unknown>>;
@@ -520,11 +502,19 @@ declare class ChatSession {
520
502
  send(body: string, attachments?: unknown[]): Promise<SbResult<unknown>>;
521
503
  /**
522
504
  * Ziyaretçinin konu seçimi. Konuşma AÇILDIKTAN sonra çağrılırsa etkisizdir:
523
- * açılmış konuşmanın konusunu ajan panelden değiştirir — ziyaretçiye kendi
505
+ * açılmış konuşmanın konusunu ajan panelden değiştirir - ziyaretçiye kendi
524
506
  * konuşmasını yeniden sınıflandırma yetkisi vermek, atamayı da bozardı.
525
507
  */
526
508
  setTopic(slug: string | null): void;
527
- /** İlk tuşta `true`, 2.5 s hareketsizlikte `false` — çağıran zamanlar. */
509
+ /**
510
+ * İlk tuşta `true`, 2.5 s hareketsizlikte `false` - çağıran zamanlar.
511
+ *
512
+ * SDK yine de kendini korur (18 Eyl 2026): `true` 4 saniyede bir kez gider,
513
+ * `false` yalnız açık bir `true` varsa. Bir entegrasyon her tuşta
514
+ * çağırdığında (Penyu mobil öyle yapıyordu) 40 harflik mesaj 40 istek
515
+ * oluyor ve sunucunun hız sınırı ikinci mesajı 429 ile düşürüyordu. Web
516
+ * widget'ındaki kuralın aynısı.
517
+ */
528
518
  typing(isTyping: boolean): void;
529
519
  /** Görülen son mesaja kadar okundu işaretler. */
530
520
  markRead(): Promise<void>;
package/dist/app.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * Bu yüzey MÜŞTERİNİN MÜŞTERİSİ içindir: ziyaretçi ya da uygulama kullanıcısı.
5
5
  * Anahtarı açıktır (`sb_public_live_…`) ve istemciye gömülür; güvenliği gizlilikten
6
- * değil kısıttan gelir — yalnız izinli kökenden çalışır ve yalnız ziyaretçinin
6
+ * değil kısıttan gelir - yalnız izinli kökenden çalışır ve yalnız ziyaretçinin
7
7
  * KENDİ verisine dokunur.
8
8
  */
9
9
  interface SbResult<T = unknown> {
@@ -33,21 +33,21 @@ interface AppConfig {
33
33
  */
34
34
  platform?: 'ios' | 'android' | 'web';
35
35
  /**
36
- * Açık domain anahtarı (`sb_public_live_…`) — KİMLİĞİ doğrular.
36
+ * Açık domain anahtarı (`sb_public_live_…`) - KİMLİĞİ doğrular.
37
37
  *
38
38
  * Panel → Alan adları → [alan adı] → Anahtarlar. Web anahtarı yalnız izinli
39
39
  * kökenlerden, mobil anahtarı yalnız Origin taşımayan isteklerden çalışır.
40
40
  */
41
41
  publicKey: string;
42
42
  /**
43
- * Sohbet kanalı — `chat` modülünün anahtarı (`destek`). DAVRANIŞI seçer:
43
+ * Sohbet kanalı - `chat` modülünün anahtarı (`destek`). DAVRANIŞI seçer:
44
44
  * hangi widget ayarı, hangi gelen kutusu.
45
45
  *
46
46
  * Anahtardan AYRIDIR ve bu bilinçlidir (1 Eyl 2026): domain anahtarını
47
47
  * yenilediğinizde kanal adı değişmez, yani kodunuz aynı kalır.
48
48
  */
49
49
  chatKey?: string;
50
- /** Push kanalı — `push` modülünün anahtarı. Cihaz kaydı için gerekir. */
50
+ /** Push kanalı - `push` modülünün anahtarı. Cihaz kaydı için gerekir. */
51
51
  pushKey?: string;
52
52
  /** Varsayılan: https://live.signalbird.io/api */
53
53
  baseUrl?: string;
@@ -73,7 +73,7 @@ interface TopicOption {
73
73
  description?: string | null;
74
74
  parent_id?: number | null;
75
75
  }
76
- /** `POST /v1/sdk/bootstrap` yanıtı — widget çizilmeden önceki tek soru. */
76
+ /** `POST /v1/sdk/bootstrap` yanıtı - widget çizilmeden önceki tek soru. */
77
77
  interface BootstrapResult {
78
78
  /**
79
79
  * Sözleşme adı `channel` (1 Eyl 2026); `app` eski sunucular için okunur.
@@ -85,10 +85,10 @@ interface BootstrapResult {
85
85
  app?: BootstrapChannel;
86
86
  /** Boşsa konu adımı hiç gösterilmez. */
87
87
  topics?: TopicOption[];
88
- /** Mevcut açık konuşma (varsa) — ChatSession ilk listelemeyi atlar. */
88
+ /** Mevcut açık konuşma (varsa) - ChatSession ilk listelemeyi atlar. */
89
89
  conversation?: Conversation | null;
90
90
  /**
91
- * Canlı bağlantı — YALNIZ ADRES. Anahtar ya da sır taşımaz: bağlanan taraf
91
+ * Canlı bağlantı - YALNIZ ADRES. Anahtar ya da sır taşımaz: bağlanan taraf
92
92
  * hiçbir şey göremez, odaya girmek imza ister ve imzayı
93
93
  * `POST /v1/sdk/chat/socket/auth` verir.
94
94
  *
@@ -139,7 +139,7 @@ interface BootstrapChannel {
139
139
  }
140
140
  interface Visitor {
141
141
  id: string;
142
- /** Ziyaretçi sırrı — YALNIZ oturum açılışında döner, sonra saklanır. */
142
+ /** Ziyaretçi sırrı - YALNIZ oturum açılışında döner, sonra saklanır. */
143
143
  secret?: string;
144
144
  name?: string | null;
145
145
  email?: string | null;
@@ -196,7 +196,7 @@ interface Message {
196
196
  edited_at?: string | null;
197
197
  created_at?: string;
198
198
  /**
199
- * Anlık çeviri — ziyaretçinin diline. Arayüz varsa bunu gösterir; orijinal
199
+ * Anlık çeviri - ziyaretçinin diline. Arayüz varsa bunu gösterir; orijinal
200
200
  * `body`'de durmaya devam eder.
201
201
  */
202
202
  translation?: {
@@ -211,7 +211,7 @@ interface Message {
211
211
  *
212
212
  * Tip listede yoktu ama oturum bu alanı yazıyordu: arayüz "gitmedi" hâlini
213
213
  * çizmek istediğinde tipi zorlamak (`as`) zorunda kalıyordu. Bekleyen mesaj
214
- * ayrıca işaretlenmez — sunucu yanıtı gelene kadar `id` ile `client_id`
214
+ * ayrıca işaretlenmez - sunucu yanıtı gelene kadar `id` ile `client_id`
215
215
  * aynıdır ve bu, "henüz yolda" demenin en ucuz yoludur.
216
216
  */
217
217
  failed?: boolean;
@@ -240,7 +240,7 @@ interface StartConversationInput {
240
240
  attachments?: unknown[];
241
241
  page_url?: string;
242
242
  /**
243
- * Destek konusu — id ya da slug. Seçim SUNUCUDA doğrulanır; geçersizse
243
+ * Destek konusu - id ya da slug. Seçim SUNUCUDA doğrulanır; geçersizse
244
244
  * konuşma yine açılır, konu yok sayılır.
245
245
  */
246
246
  topic?: string | number;
@@ -252,7 +252,7 @@ interface SendMessageInput {
252
252
  attachments?: unknown[];
253
253
  }
254
254
  interface ConversationQuery {
255
- /** `cm_…` imleci — yalnız bundan sonrakiler döner. */
255
+ /** `cm_…` imleci - yalnız bundan sonrakiler döner. */
256
256
  after?: string;
257
257
  limit?: number;
258
258
  }
@@ -267,26 +267,6 @@ interface RegisterDeviceInput {
267
267
  locale?: string;
268
268
  }
269
269
 
270
- /**
271
- * Uygulama istemcisi — son kullanıcı tarafı (sohbet + push kaydı).
272
- *
273
- * Tek bir sınıf; tarayıcı, React Native, Electron ve test aynı gövdeyi kullanır.
274
- * Platform farkı iki noktada toplanmıştır ve ikisi de dışarıdan verilir:
275
- * `storage` (ziyaretçi sırrı nerede durur) ve `fetchImpl`. Çatıya özel sarmalayıcı
276
- * yazmak yerine bunu seçtik — React, Vue, Angular ve RN uyarlamaları bu sınıfın
277
- * ÜSTÜNE oturur, kopyası değildir.
278
- *
279
- * Kimlik iki parçadır: açık domain anahtarı (`X-Signalbird-Key`) ve
280
- * ziyaretçi sırrı (`X-Signalbird-Visitor`). Sır yalnız oturum açılışında döner;
281
- * kaybolursa yeni oturum açılır ve geçmiş konuşmalar görünmez — bu yüzden
282
- * saklama katmanı zorunludur, isteğe bağlı değil.
283
- *
284
- * Hiçbir metot istisna fırlatmaz: sohbet balonunun hatası müşterinin ödeme
285
- * sayfasını çökertmemeli. Sonuç her zaman `{ok, status, …}` zarfıdır.
286
- *
287
- * Sözleşme: docs/CONTRACT.md § 11
288
- */
289
-
290
270
  /** RFC 4122 uyumlu olmak zorunda değil; tek işi yerel kopyayı eşlemek. */
291
271
  declare function clientId(): string;
292
272
  declare class SignalbirdApp {
@@ -324,7 +304,7 @@ declare class SignalbirdApp {
324
304
  identify(input: IdentifyInput): Promise<SbResult<{
325
305
  visitor: Visitor;
326
306
  }>>;
327
- /** Saklanan ziyaretçi kimliği — yoksa `null`. */
307
+ /** Saklanan ziyaretçi kimliği - yoksa `null`. */
328
308
  currentVisitor(): Promise<{
329
309
  id: string;
330
310
  name?: string | null;
@@ -343,7 +323,7 @@ declare class SignalbirdApp {
343
323
  within_hours?: boolean;
344
324
  }>>;
345
325
  /**
346
- * İlk mesajla konuşma açar. Kota burada harcanır — konuşma başına sayılır,
326
+ * İlk mesajla konuşma açar. Kota burada harcanır - konuşma başına sayılır,
347
327
  * mesaj başına değil.
348
328
  */
349
329
  startConversation(input: StartConversationInput): Promise<SbResult<{
@@ -378,21 +358,21 @@ declare class SignalbirdApp {
378
358
  rateConversation(conversationId: string, rating: number, comment?: string): Promise<SbResult<unknown>>;
379
359
  /**
380
360
  * Cihaz token'ını kaydeder. Token'ı almak (FCM/APNs/Web Push izni) ev
381
- * sahibinin işidir; SDK yalnız iletir — izin diyaloğunu kimin, ne zaman
361
+ * sahibinin işidir; SDK yalnız iletir - izin diyaloğunu kimin, ne zaman
382
362
  * göstereceği ürün kararıdır, kütüphane kararı değil.
383
363
  */
384
364
  registerDevice(input: RegisterDeviceInput): Promise<SbResult<unknown>>;
385
365
  /** Çıkışta çağrılır: kayıt silinmez, kapatılır (geçmiş korunur). */
386
366
  unregisterDevice(token: string): Promise<SbResult<unknown>>;
387
367
  /**
388
- * Bildirime dokunuldu — açılma damgası.
368
+ * Bildirime dokunuldu - açılma damgası.
389
369
  *
390
370
  * Push'ta açılmayı YALNIZCA uygulama bilir: FCM/APNs "teslim ettim" der,
391
371
  * "kullanıcı dokundu" demez. Bildirim yükündeki `data.sb_message_id`
392
372
  * değerini buraya geri gönderin.
393
373
  *
394
374
  * ```ts
395
- * // React Native / Expo — bildirime dokunma işleyicisinde
375
+ * // React Native / Expo - bildirime dokunma işleyicisinde
396
376
  * const id = response.notification.request.content.data?.sb_message_id
397
377
  * if (id) await sb.reportPushOpened(String(id))
398
378
  * ```
@@ -407,12 +387,12 @@ declare class SignalbirdApp {
407
387
  }
408
388
 
409
389
  /**
410
- * Sohbet oturumu — çatısız durum yönetimi.
390
+ * Sohbet oturumu - çatısız durum yönetimi.
411
391
  *
412
392
  * `SignalbirdApp` ham uçları verir; burası bir sohbet ekranının gerçekten
413
393
  * ihtiyaç duyduğu şeyi verir: mesaj listesi, okunmamış sayısı, yazıyor durumu,
414
394
  * iyimser gönderim ve yoklama merdiveni. React/Vue/Angular/React Native
415
- * uyarlamaları bu sınıfa abone olur — üçünde de aynı mantığı yeniden yazmak,
395
+ * uyarlamaları bu sınıfa abone olur - üçünde de aynı mantığı yeniden yazmak,
416
396
  * üç ayrı hata takımı üretmek demekti.
417
397
  *
418
398
  * ── CANLI BAĞLANTI + YOKLAMA ──────────────────────────────────────────────
@@ -450,9 +430,9 @@ interface ChatState {
450
430
  * Seçimi `setTopic()` taşır; ilk konuşma açılırken gönderilir.
451
431
  */
452
432
  topics: TopicOption[];
453
- /** Seçili konu (slug) — ilk konuşmayla birlikte gider. */
433
+ /** Seçili konu (slug) - ilk konuşmayla birlikte gider. */
454
434
  topic: string | null;
455
- /** Son hatanın kodu — arayüz isterse gösterir, göstermezse yutar. */
435
+ /** Son hatanın kodu - arayüz isterse gösterir, göstermezse yutar. */
456
436
  errorCode?: string;
457
437
  /**
458
438
  * Uygulamanın sohbet ayarları (renk, logo, tema, puanlama bağlantısı…).
@@ -464,7 +444,7 @@ interface ChatState {
464
444
  }
465
445
  type ChatListener = (state: ChatState) => void;
466
446
  interface ChatSessionOptions {
467
- /** Panel açık mı — yoklama hızını belirler. */
447
+ /** Panel açık mı - yoklama hızını belirler. */
468
448
  active?: boolean;
469
449
  /** Arka plandayken tur atlanır; varsayılan: `document.visibilityState`. */
470
450
  isVisible?: () => boolean;
@@ -491,22 +471,24 @@ declare class ChatSession {
491
471
  * da düzenleme ekrana hiç yansımaz.
492
472
  */
493
473
  private forceFull;
474
+ /** Son gönderilen `typing(true)` zamanı; 0 = açık sinyal yok. */
475
+ private lastTypingSent;
494
476
  constructor(app: SignalbirdApp, options?: ChatSessionOptions);
495
477
  subscribe(listener: ChatListener): () => void;
496
478
  snapshot(): ChatState;
497
479
  /** Bootstrap + varsa mevcut konuşmayı yükler, sonra yoklamayı başlatır. */
498
480
  start(): Promise<void>;
499
- /** Panel açıldı/kapandı — yoklama hızı buna göre değişir. */
481
+ /** Panel açıldı/kapandı - yoklama hızı buna göre değişir. */
500
482
  setActive(active: boolean): void;
501
483
  /**
502
484
  * Konuşmayı bırakır; sonraki mesaj YENİ bir konuşma açar.
503
485
  *
504
486
  * Ekranın "yeni sohbet" düğmesi de bunu çağırır. Sunucuda hiçbir şey
505
- * silinmez — yalnız bu oturumun neye baktığı değişir.
487
+ * silinmez - yalnız bu oturumun neye baktığı değişir.
506
488
  */
507
489
  reset(): void;
508
490
  stop(): void;
509
- /** Canlı bağlantı kurulu mu — arayüz isterse gösterir (zorunlu değil). */
491
+ /** Canlı bağlantı kurulu mu - arayüz isterse gösterir (zorunlu değil). */
510
492
  get isLive(): boolean;
511
493
  /** Ön-form gönderildiğinde ya da uygulama kullanıcıyı tanıdığında. */
512
494
  openSession(input: SessionInput): Promise<SbResult<unknown>>;
@@ -520,11 +502,19 @@ declare class ChatSession {
520
502
  send(body: string, attachments?: unknown[]): Promise<SbResult<unknown>>;
521
503
  /**
522
504
  * Ziyaretçinin konu seçimi. Konuşma AÇILDIKTAN sonra çağrılırsa etkisizdir:
523
- * açılmış konuşmanın konusunu ajan panelden değiştirir — ziyaretçiye kendi
505
+ * açılmış konuşmanın konusunu ajan panelden değiştirir - ziyaretçiye kendi
524
506
  * konuşmasını yeniden sınıflandırma yetkisi vermek, atamayı da bozardı.
525
507
  */
526
508
  setTopic(slug: string | null): void;
527
- /** İlk tuşta `true`, 2.5 s hareketsizlikte `false` — çağıran zamanlar. */
509
+ /**
510
+ * İlk tuşta `true`, 2.5 s hareketsizlikte `false` - çağıran zamanlar.
511
+ *
512
+ * SDK yine de kendini korur (18 Eyl 2026): `true` 4 saniyede bir kez gider,
513
+ * `false` yalnız açık bir `true` varsa. Bir entegrasyon her tuşta
514
+ * çağırdığında (Penyu mobil öyle yapıyordu) 40 harflik mesaj 40 istek
515
+ * oluyor ve sunucunun hız sınırı ikinci mesajı 429 ile düşürüyordu. Web
516
+ * widget'ındaki kuralın aynısı.
517
+ */
528
518
  typing(isTyping: boolean): void;
529
519
  /** Görülen son mesaja kadar okundu işaretler. */
530
520
  markRead(): Promise<void>;
package/dist/app.js CHANGED
@@ -1,5 +1,25 @@
1
1
  'use strict';
2
2
 
3
+ // src/shared/version.ts
4
+ var SDK_VERSION = "2.6.0" ;
5
+ var SDK_HEADER = "X-Signalbird-Sdk";
6
+ function sdkHeaderValue(platform) {
7
+ return `${platform}/${SDK_VERSION}`;
8
+ }
9
+ var warned = false;
10
+ function noteSdkStatus(headers) {
11
+ if (warned || !headers) return;
12
+ try {
13
+ const status = headers.get("Signalbird-Sdk-Status");
14
+ if (status !== "outdated" && status !== "unsupported") return;
15
+ warned = true;
16
+ const latest = headers.get("Signalbird-Sdk-Latest") ?? "?";
17
+ const message = status === "unsupported" ? `[signalbird] Bu SDK s\xFCr\xFCm\xFC (${SDK_VERSION}) art\u0131k desteklenmiyor. Son s\xFCr\xFCm: ${latest}. Paketi g\xFCncelleyin.` : `[signalbird] Yeni SDK s\xFCr\xFCm\xFC var: ${latest} (kurulu: ${SDK_VERSION}).`;
18
+ if (typeof console !== "undefined") console.warn(message);
19
+ } catch {
20
+ }
21
+ }
22
+
3
23
  // src/app/client.ts
4
24
  var DEFAULT_BASE_URL = "https://live.signalbird.io/api";
5
25
  var STORAGE_KEY = "sb_visitor";
@@ -87,7 +107,7 @@ var SignalbirdApp = class {
87
107
  identify(input) {
88
108
  return this.request("POST", "/v1/sdk/identify", input);
89
109
  }
90
- /** Saklanan ziyaretçi kimliği — yoksa `null`. */
110
+ /** Saklanan ziyaretçi kimliği - yoksa `null`. */
91
111
  async currentVisitor() {
92
112
  const stored = await this.loadVisitor();
93
113
  return stored ? { id: stored.id, name: stored.name, email: stored.email } : null;
@@ -106,7 +126,7 @@ var SignalbirdApp = class {
106
126
  return this.request("GET", `/v1/sdk/chat/conversations/${enc(id)}`, void 0, query);
107
127
  }
108
128
  /**
109
- * İlk mesajla konuşma açar. Kota burada harcanır — konuşma başına sayılır,
129
+ * İlk mesajla konuşma açar. Kota burada harcanır - konuşma başına sayılır,
110
130
  * mesaj başına değil.
111
131
  */
112
132
  startConversation(input) {
@@ -180,7 +200,7 @@ var SignalbirdApp = class {
180
200
  // ── Push ──────────────────────────────────────────────────────────────
181
201
  /**
182
202
  * Cihaz token'ını kaydeder. Token'ı almak (FCM/APNs/Web Push izni) ev
183
- * sahibinin işidir; SDK yalnız iletir — izin diyaloğunu kimin, ne zaman
203
+ * sahibinin işidir; SDK yalnız iletir - izin diyaloğunu kimin, ne zaman
184
204
  * göstereceği ürün kararıdır, kütüphane kararı değil.
185
205
  */
186
206
  registerDevice(input) {
@@ -191,14 +211,14 @@ var SignalbirdApp = class {
191
211
  return this.request("DELETE", `/v1/sdk/devices/${enc(token)}`);
192
212
  }
193
213
  /**
194
- * Bildirime dokunuldu — açılma damgası.
214
+ * Bildirime dokunuldu - açılma damgası.
195
215
  *
196
216
  * Push'ta açılmayı YALNIZCA uygulama bilir: FCM/APNs "teslim ettim" der,
197
217
  * "kullanıcı dokundu" demez. Bildirim yükündeki `data.sb_message_id`
198
218
  * değerini buraya geri gönderin.
199
219
  *
200
220
  * ```ts
201
- * // React Native / Expo — bildirime dokunma işleyicisinde
221
+ * // React Native / Expo - bildirime dokunma işleyicisinde
202
222
  * const id = response.notification.request.content.data?.sb_message_id
203
223
  * if (id) await sb.reportPushOpened(String(id))
204
224
  * ```
@@ -215,7 +235,8 @@ var SignalbirdApp = class {
215
235
  const isForm = typeof FormData !== "undefined" && body instanceof FormData;
216
236
  const headers = {
217
237
  Accept: "application/json",
218
- "X-Signalbird-Key": this.config.publicKey
238
+ "X-Signalbird-Key": this.config.publicKey,
239
+ [SDK_HEADER]: sdkHeaderValue(appPlatform())
219
240
  };
220
241
  const moduleKey = path.startsWith("/v1/sdk/devices") || path.startsWith("/v1/sdk/push") ? this.config.pushKey : this.config.chatKey;
221
242
  if (moduleKey) headers["X-Signalbird-Module-Key"] = moduleKey;
@@ -231,6 +252,7 @@ var SignalbirdApp = class {
231
252
  body: body === void 0 ? void 0 : isForm ? body : JSON.stringify(body),
232
253
  signal: controller.signal
233
254
  });
255
+ noteSdkStatus(response.headers);
234
256
  const text = await response.text();
235
257
  let data = null;
236
258
  try {
@@ -306,6 +328,9 @@ function buildQuery(query) {
306
328
  const encoded = params.toString();
307
329
  return encoded ? `?${encoded}` : "";
308
330
  }
331
+ function appPlatform() {
332
+ return typeof navigator !== "undefined" && navigator.product === "ReactNative" ? "react-native" : "app";
333
+ }
309
334
 
310
335
  // src/shared/socket.ts
311
336
  var BACKOFF = [1e3, 2e3, 5e3, 1e4, 3e4];
@@ -430,7 +455,7 @@ var Socket = class {
430
455
  }
431
456
  }
432
457
  /**
433
- * Olay yolla — ACK İSTEMEDEN.
458
+ * Olay yolla - ACK İSTEMEDEN.
434
459
  *
435
460
  * `subscribe`in sonucunu beklemek bir tur daha protokol yönetmek demekti;
436
461
  * oysa sonucu zaten davranıştan görüyoruz: imza tutmadıysa yayın gelmez ve
@@ -495,6 +520,8 @@ var ChatSession = class {
495
520
  * da düzenleme ekrana hiç yansımaz.
496
521
  */
497
522
  this.forceFull = false;
523
+ /** Son gönderilen `typing(true)` zamanı; 0 = açık sinyal yok. */
524
+ this.lastTypingSent = 0;
498
525
  this.active = options.active ?? false;
499
526
  }
500
527
  // ── Abonelik ──────────────────────────────────────────────────────────
@@ -529,7 +556,7 @@ var ChatSession = class {
529
556
  this.patch({ loading: false });
530
557
  this.schedule();
531
558
  }
532
- /** Panel açıldı/kapandı — yoklama hızı buna göre değişir. */
559
+ /** Panel açıldı/kapandı - yoklama hızı buna göre değişir. */
533
560
  setActive(active) {
534
561
  if (this.active === active) return;
535
562
  this.active = active;
@@ -544,7 +571,7 @@ var ChatSession = class {
544
571
  * Konuşmayı bırakır; sonraki mesaj YENİ bir konuşma açar.
545
572
  *
546
573
  * Ekranın "yeni sohbet" düğmesi de bunu çağırır. Sunucuda hiçbir şey
547
- * silinmez — yalnız bu oturumun neye baktığı değişir.
574
+ * silinmez - yalnız bu oturumun neye baktığı değişir.
548
575
  */
549
576
  reset() {
550
577
  this.patch({ conversation: null, messages: [], unread: 0, agentTyping: false, errorCode: void 0 });
@@ -558,7 +585,7 @@ var ChatSession = class {
558
585
  this.socket = null;
559
586
  this.live = false;
560
587
  }
561
- /** Canlı bağlantı kurulu mu — arayüz isterse gösterir (zorunlu değil). */
588
+ /** Canlı bağlantı kurulu mu - arayüz isterse gösterir (zorunlu değil). */
562
589
  get isLive() {
563
590
  return this.live;
564
591
  }
@@ -613,16 +640,32 @@ var ChatSession = class {
613
640
  }
614
641
  /**
615
642
  * Ziyaretçinin konu seçimi. Konuşma AÇILDIKTAN sonra çağrılırsa etkisizdir:
616
- * açılmış konuşmanın konusunu ajan panelden değiştirir — ziyaretçiye kendi
643
+ * açılmış konuşmanın konusunu ajan panelden değiştirir - ziyaretçiye kendi
617
644
  * konuşmasını yeniden sınıflandırma yetkisi vermek, atamayı da bozardı.
618
645
  */
619
646
  setTopic(slug) {
620
647
  this.patch({ topic: slug });
621
648
  }
622
- /** İlk tuşta `true`, 2.5 s hareketsizlikte `false` — çağıran zamanlar. */
649
+ /**
650
+ * İlk tuşta `true`, 2.5 s hareketsizlikte `false` - çağıran zamanlar.
651
+ *
652
+ * SDK yine de kendini korur (18 Eyl 2026): `true` 4 saniyede bir kez gider,
653
+ * `false` yalnız açık bir `true` varsa. Bir entegrasyon her tuşta
654
+ * çağırdığında (Penyu mobil öyle yapıyordu) 40 harflik mesaj 40 istek
655
+ * oluyor ve sunucunun hız sınırı ikinci mesajı 429 ile düşürüyordu. Web
656
+ * widget'ındaki kuralın aynısı.
657
+ */
623
658
  typing(isTyping) {
624
659
  const conversation = this.state.conversation;
625
660
  if (!conversation) return;
661
+ const now = Date.now();
662
+ if (isTyping) {
663
+ if (now - this.lastTypingSent < 4e3) return;
664
+ this.lastTypingSent = now;
665
+ } else {
666
+ if (this.lastTypingSent === 0) return;
667
+ this.lastTypingSent = 0;
668
+ }
626
669
  void this.app.setTyping(conversation.id, isTyping);
627
670
  }
628
671
  /** Görülen son mesaja kadar okundu işaretler. */