signalbird 1.4.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.
package/README.md ADDED
@@ -0,0 +1,576 @@
1
+ # Signalbird SDK
2
+
3
+ **Tek paket, dört yüzey, on iki giriş noktası.** Panelde tıklayarak yapabildiğiniz her şey
4
+ kodla da yapılabilir.
5
+
6
+ | Yüzey | Ne yapar | Anahtar | Nerede |
7
+ |---|---|---|---|
8
+ | **Telsiz** (Radio) | projenizden bir **kanala** log/olay yazar | `sbr_live_…` / `sbr_pub_…` | sunucu / tarayıcı |
9
+ | **Gönderim** (Messaging) | e-posta, SMS, push gönderir; kişi, liste, kampanya yönetir; mesaj durumu okur; webhook imzası doğrular | `sb_…` | yalnız sunucu |
10
+ | **Yönetim** (Management) | Telsiz projesi/kanalı açar, olay akışını okur, **sohbet gelen kutusunu** işler, uygulama ve cihaz yönetir | `sb_…` + scope | yalnız sunucu |
11
+ | **Uygulama** (App) | müşterinizin **son kullanıcısına** canlı sohbet + push cihaz kaydı | `sbw_pub_…` | web, iOS, Android |
12
+ | **Partner** | Signalbird'ü kendi ürününde satan **sözleşmeli platform** müşterisini sağlar ve yetkilendirir | `sbp_live_…` | yalnız sunucu |
13
+
14
+ Bir seçenek daha var ve kod yazmaz: hazır sohbet widget'ı
15
+ (`signalbird.js`), siteye tek `<script>` ile gömülür.
16
+
17
+ ### Dil matrisi
18
+
19
+ | Dil / platform | Telsiz | Gönderim | Yönetim | Uygulama | Kurulum |
20
+ |---|:--:|:--:|:--:|:--:|---|
21
+ | Node.js / TypeScript | ✓ | ✓ | ✓ | ✓ | `npm i signalbird` |
22
+ | Tarayıcı (düz JS) | ✓ | — | — | ✓ | `signalbird/browser` · `/app` |
23
+ | React / Next.js | ✓ | ✓ | ✓ | ✓ | `signalbird/react` |
24
+ | Vue 3 | ✓ | — | — | ✓ | `signalbird/vue` |
25
+ | Angular | ✓ | — | — | ✓ | `signalbird/angular` |
26
+ | React Native / Expo | ✓ | — | — | ✓ | `signalbird/react-native` |
27
+ | PHP / Laravel | ✓ | ✓ | ✓ | — | `composer require pariette/signalbird` |
28
+ | Python | ✓ | ✓ | ✓ | — | `pip install signalbird` |
29
+ | Go | ✓ | ✓ | ✓ | — | `go get github.com/Pariette-Inc/signalbird.sdk` |
30
+ | .NET / ASP.NET Core | ✓ | ✓ | ✓ | — | `dotnet add package Signalbird.Sdk` |
31
+ | Swift (iOS) | ✓ | — | — | ✓ | SPM: `Signalbird` |
32
+ | Kotlin (Android) | ✓ | — | — | ✓ | `io.signalbird:signalbird-sdk` |
33
+
34
+ Metot adları diller arasında **birebir** aynıdır; her dil kendi yazım
35
+ geleneğini korur (`createRadioProject` / `create_radio_project` /
36
+ `CreateRadioProject`). `node scripts/check-parity.mjs` bunu her derlemede
37
+ denetler.
38
+
39
+ Uygulama yüzeyi mobil ve tarayıcı içindir: gizli anahtar oraya gömülmez.
40
+ Gönderim ve Yönetim yüzeyleri yalnız sunucudadır.
41
+
42
+ ## Telsiz
43
+
44
+ Telsiz'in tek işi vardır: projenizden bir **kanala** mesaj yazmak.
45
+
46
+ Bildirimin kime gideceği, hangi kanaldan (push/e-posta), sessiz saatlerde ne
47
+ olacağı ve aynı mesajın kaç kez uyarı üreteceği **sunucuda, kanal ayarlarında**
48
+ durur. Kod tarafında bunlar yoktur ve olmamalıdır: bildirim kuralını
49
+ değiştirmek için uygulamanızı yeniden yayınlamanız gerekmesin.
50
+
51
+ ```
52
+ proje → penyu.io (anahtarın sahibi)
53
+ kanal → critical, info, deploy… (bildirim kuralı burada)
54
+ olay → tek bir kayıt
55
+ ```
56
+
57
+ ## İki anahtar, iki paket
58
+
59
+ | | Sunucu | Tarayıcı |
60
+ |---|---|---|
61
+ | Anahtar | `sbr_live_…` **gizli** | `sbr_pub_…` **açık** |
62
+ | Giriş noktası | `signalbird` · `pariette/signalbird` | `signalbird/browser` |
63
+ | Yazabildiği kanal | hepsi | yalnız izin verilenler |
64
+ | Kısıt | — | yalnız izinli alan adlarından |
65
+
66
+ Gizli anahtar tarayıcıya **gömülemez**: sunucu, `Origin` başlığı taşıyan bir
67
+ istekte gizli anahtarı reddeder (`SECRET_KEY_IN_BROWSER`). Bu bir kolaylık
68
+ değil, kasıtlı bir duvardır — anahtar bir kez istemciye indiğinde herkesindir.
69
+
70
+ ## Kurulum
71
+
72
+ | Dil / çatı | Kurulum |
73
+ |---|---|
74
+ | Node.js, Next.js (sunucu), Express, NestJS, Fastify | `npm install signalbird` |
75
+ | React, Vue, Angular, Svelte, düz JS (tarayıcı) | `npm install signalbird` → `/browser`, `/app`, `/react`, `/vue`, `/angular` |
76
+ | React Native, Expo | `npm install signalbird` → `/react-native` |
77
+ | PHP, Laravel | `composer require pariette/signalbird` |
78
+ | Python (Django, FastAPI, Flask, Celery) | `pip install signalbird` |
79
+ | Go | `go get github.com/Pariette-Inc/signalbird.sdk` |
80
+ | .NET, ASP.NET Core | `dotnet add package Signalbird.Sdk` |
81
+ | Swift (iOS, macOS) | SPM: `https://github.com/Pariette-Inc/signalbird.sdk` |
82
+ | Kotlin (Android) | `implementation("io.signalbird:signalbird-sdk:1.2.0")` |
83
+ | Canlı sohbet widget'ı (herhangi bir site) | `<script async src="https://signalbird.io/sdk/v1/signalbird.js" data-app-key="sbw_pub_…"></script>` |
84
+
85
+ > Hepsi **bu repodan** çıkar ve **aynı sürümü** taşır — ayrı SDK reposu ya da
86
+ > dil başına sürüm yoktur.
87
+
88
+ ## Node.js / TypeScript
89
+
90
+ ```ts
91
+ import { signalbird } from 'signalbird'
92
+
93
+ // SIGNALBIRD_KEY ortam değişkeninden okunur
94
+ await signalbird().critical('critical', 'ödeme servisi yanıt vermiyor', {
95
+ service: 'iyzico',
96
+ attempt: 3,
97
+ })
98
+
99
+ await signalbird().info('info', 'ahmet@x.com yeni hesap oluşturdu')
100
+ ```
101
+
102
+ Kendi istemcinizi kurmak isterseniz:
103
+
104
+ ```ts
105
+ import { SignalbirdClient } from 'signalbird'
106
+
107
+ const sb = new SignalbirdClient({
108
+ apiKey: process.env.SIGNALBIRD_KEY!,
109
+ source: 'api-01', // hangi sunucudan geldiği
110
+ throwOnError: false, // üretimde kapalı kalmalı
111
+ })
112
+
113
+ await sb.log({ channel: 'deploy', message: 'v2.4.0 yayında', level: 'info' })
114
+ ```
115
+
116
+ **Yakalanmamış hatalar:**
117
+
118
+ ```ts
119
+ signalbird().captureUncaught('critical')
120
+ ```
121
+
122
+ **Toplu gönderim** (kısmi başarı normaldir, satır satır sonuç döner):
123
+
124
+ ```ts
125
+ const result = await signalbird().batch([
126
+ { channel: 'info', message: 'iş 1 bitti' },
127
+ { channel: 'info', message: 'iş 2 bitti' },
128
+ ])
129
+ ```
130
+
131
+ ### Next.js
132
+
133
+ Sunucu tarafında (route handler, server action, `app/api/**`) doğrudan
134
+ `signalbird` kullanılır. **İstemci bileşenlerinde kullanmayın** — anahtar
135
+ paketle birlikte tarayıcıya iner.
136
+
137
+ ```ts
138
+ // app/api/webhook/route.ts
139
+ import { signalbird } from 'signalbird'
140
+
141
+ export async function POST(req: Request) {
142
+ try {
143
+ // …
144
+ } catch (error) {
145
+ await signalbird().error('webhook', (error as Error).message)
146
+ throw error
147
+ }
148
+ }
149
+ ```
150
+
151
+ ## Tarayıcı (React, Vue, Angular, düz JS)
152
+
153
+ Çatıya özel sarmalayıcı yoktur; gereken tek şey bir fonksiyon çağrısıdır.
154
+
155
+ ```ts
156
+ // uygulama açılışında bir kez
157
+ import { initSignalbird } from 'signalbird/browser'
158
+
159
+ const sb = initSignalbird({
160
+ publicKey: 'sbr_pub_…',
161
+ source: 'web',
162
+ })
163
+
164
+ sb.captureErrors('browser') // window.onerror + unhandledrejection
165
+ sb.error('browser', 'sepet güncellenemedi', { cartId })
166
+ ```
167
+
168
+ Kayıtlar tek tek değil, **toplu** gider (varsayılan 3 saniyede bir) ve sekme
169
+ kapanırken `sendBeacon` ile boşaltılır.
170
+
171
+ **React** — `app/providers.tsx` ya da `main.tsx`:
172
+
173
+ ```tsx
174
+ useEffect(() => {
175
+ const sb = initSignalbird({ publicKey: process.env.NEXT_PUBLIC_SIGNALBIRD_KEY! })
176
+ return sb.captureErrors()
177
+ }, [])
178
+ ```
179
+
180
+ **Vue** — `main.ts`:
181
+
182
+ ```ts
183
+ const sb = initSignalbird({ publicKey: import.meta.env.VITE_SIGNALBIRD_KEY })
184
+ app.config.errorHandler = (err) => sb.error('browser', String(err))
185
+ ```
186
+
187
+ **Angular** — `ErrorHandler` sağlayıcısı:
188
+
189
+ ```ts
190
+ @Injectable()
191
+ export class SignalbirdErrorHandler implements ErrorHandler {
192
+ private sb = initSignalbird({ publicKey: environment.signalbirdKey })
193
+ handleError(error: unknown) { this.sb.error('browser', String(error)) }
194
+ }
195
+ ```
196
+
197
+ Panelde bu projenin **izinli kökenlerini** ve **izinli kanallarını** açmayı
198
+ unutmayın; ikisi de boşken tarayıcı anahtarı hiçbir şey yapamaz. Kritik
199
+ kanalları tarayıcıya açmayın: istemci kodu herkesin elindedir.
200
+
201
+ ## PHP / Laravel
202
+
203
+ ```php
204
+ use Signalbird\Sdk\Facades\Signalbird;
205
+
206
+ Signalbird::critical('critical', 'ödeme servisi yanıt vermiyor', [
207
+ 'service' => 'iyzico',
208
+ ]);
209
+
210
+ Signalbird::info('info', 'ahmet@x.com yeni hesap oluşturdu');
211
+ ```
212
+
213
+ `.env`:
214
+
215
+ ```
216
+ SIGNALBIRD_KEY=sbr_live_…
217
+ SIGNALBIRD_SOURCE=api-01
218
+ ```
219
+
220
+ **Laravel'in kendi loglarını Telsiz'e bağlamak** — `config/logging.php`:
221
+
222
+ ```php
223
+ 'signalbird' => [
224
+ 'driver' => 'monolog',
225
+ 'handler' => \Signalbird\Sdk\SignalbirdLogHandler::class,
226
+ 'with' => ['channel' => 'laravel'],
227
+ 'level' => 'error',
228
+ ],
229
+ ```
230
+
231
+ Sonra `LOG_STACK=single,signalbird`. Mevcut `Log::error()` satırlarınız olduğu
232
+ gibi çalışır; tek satır kod yazmadan Telsiz'e düşerler.
233
+
234
+ Laravel dışı PHP:
235
+
236
+ ```php
237
+ use Signalbird\Sdk\Signalbird;
238
+
239
+ Signalbird::configure('sbr_live_…');
240
+ Signalbird::error('api', 'veritabanı bağlantısı koptu');
241
+ ```
242
+
243
+ ## Davranış kuralları
244
+
245
+ - **Sessiz hata varsayılandır.** Telsiz erişilemezse çağrı `ok: false` döner ve
246
+ uygulamanız çalışmaya devam eder. Log göndermek, ödeme akışını çökertmek için
247
+ geçerli bir sebep değildir. Geliştirme sırasında `throwOnError: true`.
248
+ - **Tanımsız kanal düşürülmez.** İlk `log('odeme-hatasi', …)` çağrısında kanal
249
+ kendiliğinden açılır ve panelde "otomatik açıldı" işaretiyle görünür. Yeni
250
+ kanal **sessizdir** — kuralı ekip koyar.
251
+ - **Seviye kanalın varsayılanını ezer.** `level` göndermezseniz kanalın kendi
252
+ seviyesi geçerlidir.
253
+ - **Kritik seviye sessiz saatleri deler.** Gece üçte ölen servis sabahı bekleyemez.
254
+ - **Tekrar bastırma kaydı değil bildirimi susturur.** Aynı mesaj kanalın
255
+ `dedupe` süresi içinde tekrar gelirse ikinci bildirim gitmez ama kayıt tutulur.
256
+
257
+ ## Gönderim (Messaging)
258
+
259
+ Takım API anahtarı (`sb_…`, panelde **Konsol → API anahtarları**, scope'lu)
260
+ ile çalışır. Telsiz anahtarı burada geçmez — istemci kurulurken
261
+ `WRONG_KEY_TYPE` ile reddeder. Yalnız sunucuda kullanılır.
262
+
263
+ Node:
264
+
265
+ ```ts
266
+ import { SignalbirdMessaging } from 'signalbird'
267
+
268
+ const sb = new SignalbirdMessaging({ apiKey: process.env.SIGNALBIRD_MESSAGING_KEY! })
269
+
270
+ const r = await sb.sendEmail({
271
+ to: 'ali@example.com',
272
+ class: 'transactional', // zorunlu: transactional | commercial
273
+ subject: 'Siparişiniz yola çıktı',
274
+ body: '<p>Merhaba {{first_name}}…</p>',
275
+ })
276
+ if (!r.ok) console.error(r.code, r.message) // ok:false → code + message
277
+
278
+ await sb.sendSms({ to: '+905551112233', class: 'transactional', body: 'Kodunuz: 4821' })
279
+ await sb.sendPush({ to: 'external:user-1042', class: 'transactional', subject: 'Yeni mesaj', body: '…' })
280
+
281
+ // Kişi + liste + kampanya
282
+ const list = await sb.createContactList({ name: 'agustos-kampanya' })
283
+ await sb.bulkContacts({ // 1000'lik parçalara bölünür
284
+ list_id: list.data.id,
285
+ consent_source: 'offline',
286
+ contacts: [{ email: 'a@x.com', first_name: 'Ayşe', attributes: { external_ref: 'rcp_1' } }],
287
+ })
288
+ const c = await sb.createCampaign({
289
+ name: 'Ağustos', channel: 'email', list_id: list.data.id,
290
+ subject: 'Merhaba {{first_name}}', body: '…', external_ref: 'cc_42',
291
+ })
292
+ for await (const m of sb.iterateCampaignMessages(c.data.batch.id)) {
293
+ console.log(m.external_ref, m.status)
294
+ }
295
+ ```
296
+
297
+ PHP / Laravel:
298
+
299
+ ```php
300
+ use Signalbird\Sdk\Facades\Signalbird;
301
+
302
+ $r = Signalbird::messaging()->sendEmail([
303
+ 'to' => 'ali@example.com', 'class' => 'transactional',
304
+ 'subject' => 'Siparişiniz yola çıktı', 'body' => '<p>…</p>',
305
+ ]);
306
+ if (! $r['ok']) { Log::warning($r['code'], $r); }
307
+ ```
308
+
309
+ `.env`: `SIGNALBIRD_MESSAGING_KEY=sb_…` (isteğe bağlı `SIGNALBIRD_MESSAGING_URL`,
310
+ `SIGNALBIRD_MESSAGING_TIMEOUT`). Laravel dışı PHP:
311
+ `Signalbird::configureMessaging('sb_…')` ya da `new MessagingClient('sb_…')`.
312
+
313
+ Metot kümesi iki dilde aynıdır: `sendEmail` `sendSms` `previewSms` `sendPush` ·
314
+ `listContacts` `createContact` `updateContact` `deleteContact` `bulkContacts` ·
315
+ `listContactLists` `createContactList` `deleteContactList` · `listCampaigns`
316
+ `createCampaign` `getCampaign` `cancelCampaign` `listCampaignMessages`
317
+ `iterateCampaignMessages` · `listMessages` `getMessage`. Hepsi
318
+ `{ok, status, data?, code?, message?}` döner; `throwOnError: true` ile istisna
319
+ (`SignalbirdError` / `SignalbirdException`, `code` + `status` + `body` taşır).
320
+
321
+ **Webhook imzası** (`message.*`, `campaign.*` olayları):
322
+
323
+ ```ts
324
+ import { verifyWebhook } from 'signalbird'
325
+ // Express: app.post('/hooks/signalbird', express.raw({ type: '*/*' }), (req, res) => {
326
+ if (!verifyWebhook(req.body, req.header('X-Signalbird-Signature'), process.env.SIGNALBIRD_WEBHOOK_SECRET!)) {
327
+ return res.status(401).end()
328
+ }
329
+ ```
330
+
331
+ ```php
332
+ use Signalbird\Sdk\Messaging\Webhook;
333
+
334
+ abort_unless(Webhook::verify($request->getContent(), $request->header('X-Signalbird-Signature'), config('services.signalbird.webhook_secret')), 401);
335
+ ```
336
+
337
+ Doğrulama **ham gövde** üzerinde yapılır; JSON'u ayrıştırıp yeniden
338
+ serileştirmek imzayı bozar.
339
+
340
+ ## Yönetim (Management)
341
+
342
+ Panelde tıklayarak yaptığınız her şeyi kodla yapar. Ortam kurulumunuz, CI
343
+ akışınız ya da kendi ajan arayüzünüz artık panel oturumu taklit etmek zorunda
344
+ değil.
345
+
346
+ **Bu bir admin yüzeyi değildir:** anahtar tek bir takıma bağlıdır ve yalnız o
347
+ takımın kayıtlarına dokunur. Kullanıcı, faturalama ve abonelik işlemleri SDK'da
348
+ yoktur.
349
+
350
+ Panelden `radio:*`, `chat:*`, `apps:*` scope'larıyla bir `sb_…` anahtarı açın.
351
+
352
+ ```ts
353
+ import { management } from 'signalbird'
354
+
355
+ // Yeni ortam kurulumu: proje aç, kanalını tanımla, anahtarı sakla
356
+ const { data } = await management().createRadioProject({ name: 'ödeme-servisi' })
357
+
358
+ // `secret` YALNIZ burada döner — sunucuda yalnız özeti saklanır
359
+ await vault.write('SIGNALBIRD_KEY', data!.secret)
360
+
361
+ await management().createRadioChannel(data!.project.id, {
362
+ key: 'odeme',
363
+ name: 'Ödeme',
364
+ level: 'critical',
365
+ notify_push: true,
366
+ quiet_from: 0,
367
+ quiet_to: 7, // kritik seviye sessiz saatleri yine de deler
368
+ })
369
+ ```
370
+
371
+ Sohbet gelen kutusunu kendi botunuzla işleyin:
372
+
373
+ ```ts
374
+ const inbox = await management().listConversations({ status: 'open', per_page: 20 })
375
+
376
+ for (const conversation of inbox.data?.data ?? []) {
377
+ await management().reply(conversation.id, {
378
+ body: 'Merhaba! Ekibimiz birkaç dakika içinde yanıtlayacak.',
379
+ })
380
+
381
+ // İç not: gelen kutusunda görünür, ziyaretçiye ASLA gitmez
382
+ await management().reply(conversation.id, { body: 'Bot yanıtladı', is_internal: true })
383
+ }
384
+ ```
385
+
386
+ Aynısı PHP, Python, Go ve .NET'te birebir aynı metot adlarıyla:
387
+
388
+ ```php
389
+ Signalbird::management()->createRadioProject(['name' => 'ödeme-servisi']);
390
+ ```
391
+
392
+ ```python
393
+ signalbird.SignalbirdManagement(api_key=key).create_radio_project({"name": "ödeme-servisi"})
394
+ ```
395
+
396
+ ```go
397
+ admin.CreateRadioProject(ctx, map[string]any{"name": "ödeme-servisi"})
398
+ ```
399
+
400
+ ```csharp
401
+ await management.CreateRadioProjectAsync(new { name = "ödeme-servisi" });
402
+ ```
403
+
404
+ Tam liste (40 metot): `docs/CONTRACT.md § 10`.
405
+
406
+ ## Partner — müşteri sağlama
407
+
408
+ Signalbird'ü kendi ürününüzün içinde satıyorsanız (sözleşmeli platform) bu yüzey
409
+ sizindir: müşteri hesabı açar, domain ekleyip izlemeye alır, uptime okur, ödeme
410
+ alındığında modül açar ve panel ekranını kendi sayfanıza gömersiniz.
411
+
412
+ ```ts
413
+ import { SignalbirdPartner } from 'signalbird'
414
+
415
+ const partner = new SignalbirdPartner({ apiKey: process.env.SIGNALBIRD_PARTNER_KEY! })
416
+
417
+ // Müşteri açıldı — idempotent: aynı external_id ikinci kez yeni hesap AÇMAZ
418
+ const { data } = await partner.createCompany({
419
+ external_id: 'sc_9911',
420
+ name: 'Acme',
421
+ owner: { email: 'sahip@acme.com', name: 'Acme Sahibi', external_id: 'u_88' },
422
+ })
423
+
424
+ // Domain açıldı → anında izlemeye girsin
425
+ await partner.addDomain('sc_9911', {
426
+ external_id: 'd_5',
427
+ domain: 'acme.com',
428
+ monitoring: { enabled: true, frequency: 5 },
429
+ })
430
+
431
+ // Ödeme alındı → modül açılsın
432
+ await partner.grantModule('sc_9911', { module: 'email', expires_at: '2027-08-20' })
433
+
434
+ // Kendi domain listesi ekranınızda uptime
435
+ const uptime = await partner.companyUptime('sc_9911', '7d')
436
+
437
+ // Sohbet ekranını kendi sayfanıza gömün (jetonu SUNUCUNUZ üretir)
438
+ const embed = await partner.createEmbedToken('sc_9911', {
439
+ user_external_id: 'u_88', module: 'chat', theme: 'dark',
440
+ })
441
+ // → <iframe src={embed.data.url} />
442
+ ```
443
+
444
+ PHP'de `Signalbird::partner()->createCompany([...])`.
445
+
446
+ İki kural: **anahtar tarayıcıya inmez** ve **TXT'siz domain kampanya
447
+ gönderemez** (izleme, sohbet ve push açıktır). Ayrıntı: `docs/CONTRACT.md § 12`.
448
+
449
+ ## Uygulama (App) — kendi sohbet arayüzünüz
450
+
451
+ Hazır widget yerine kendi arayüzünüzü yazmak, ya da sohbeti **mobil
452
+ uygulamanıza** koymak istiyorsanız bu yüzey içindir. Açık uygulama anahtarı
453
+ (`sbw_pub_…`) kullanır ve yalnız ziyaretçinin kendi verisine dokunur.
454
+
455
+ **React / Next.js**
456
+
457
+ ```tsx
458
+ import { SignalbirdProvider, useChat } from 'signalbird/react'
459
+
460
+ export function App() {
461
+ return (
462
+ <SignalbirdProvider appKey={process.env.NEXT_PUBLIC_SIGNALBIRD_APP_KEY!}>
463
+ <Chat />
464
+ </SignalbirdProvider>
465
+ )
466
+ }
467
+
468
+ function Chat() {
469
+ const { messages, unread, agentTyping, send } = useChat({ open: true })
470
+
471
+ return (
472
+ <>
473
+ {messages.map((m) => <Bubble key={m.id} message={m} />)}
474
+ {agentTyping && <Typing />}
475
+ <Composer onSend={send} />
476
+ </>
477
+ )
478
+ }
479
+ ```
480
+
481
+ **Vue 3**
482
+
483
+ ```ts
484
+ app.use(signalbirdPlugin, { appKey: import.meta.env.VITE_SIGNALBIRD_APP_KEY })
485
+
486
+ const { state, send } = useChat({ open: isOpen })
487
+ ```
488
+
489
+ **Angular**
490
+
491
+ ```ts
492
+ bootstrapApplication(App, { providers: [provideSignalbird({ appKey })] })
493
+
494
+ // bileşende
495
+ chat$ = inject(SignalbirdService).chat$()
496
+ ```
497
+
498
+ **React Native / Expo**
499
+
500
+ ```tsx
501
+ import AsyncStorage from '@react-native-async-storage/async-storage'
502
+ import { createSignalbirdApp, asyncStorageAdapter, useNativeChat } from 'signalbird/react-native'
503
+
504
+ // Depoyu vermek ZORUNLU: sır cihazda kalmazsa geçmiş her açılışta kaybolur
505
+ const client = createSignalbirdApp({ appKey, storage: asyncStorageAdapter(AsyncStorage) })
506
+
507
+ const { messages, send } = useNativeChat(client, { open: true, isForeground })
508
+ ```
509
+
510
+ **Swift (iOS)**
511
+
512
+ ```swift
513
+ let client = try SignalbirdApp(config: .init(appKey: "sbw_pub_…"))
514
+
515
+ try await client.startSession(["name": "Ayşe"])
516
+ try await client.startConversation(body: "Kargom nerede?")
517
+ try await client.registerDevice(token: apnsToken)
518
+ ```
519
+
520
+ **Kotlin (Android)**
521
+
522
+ ```kotlin
523
+ val client = SignalbirdApp(SignalbirdAppConfig(appKey = "sbw_pub_…", storage = prefsStorage))
524
+
525
+ client.startSession(mapOf("name" to "Ayşe"))
526
+ client.startConversation("Kargom nerede?")
527
+ client.registerDevice(token = fcmToken)
528
+ ```
529
+
530
+ Tam liste (17 metot) ve yoklama merdiveni: `docs/CONTRACT.md § 11`.
531
+
532
+ ## Widget (canlı sohbet)
533
+
534
+ Panelde **Gelen Kutusu → Ayarlar → Uygulamalar**'dan bir uygulama açın; verilen
535
+ `sbw_pub_…` anahtarını sitenize gömün:
536
+
537
+ ```html
538
+ <script async src="https://signalbird.io/sdk/v1/signalbird.js" data-app-key="sbw_pub_…"></script>
539
+ ```
540
+
541
+ Bu kadar. Sohbet modülü açıksa balon görünür; renk, konum, karşılama, ön-form,
542
+ çalışma saatleri panelden yönetilir. Programatik kullanım:
543
+
544
+ ```js
545
+ Signalbird.identify({ external_id: 'user-1042', email: 'ali@example.com', name: 'Ali Veli' })
546
+ Signalbird.chat.open() // close() · toggle() · isOpen()
547
+ Signalbird.chat.on('unread', (n) => badge.textContent = n)
548
+ Signalbird.push.register({ token, platform: 'web', provider: 'fcm' })
549
+ Signalbird.destroy()
550
+ ```
551
+
552
+ `data-app-key` yerine `Signalbird.init({ appKey, baseUrl?, locale? })` da
553
+ çağrılabilir. Widget ev sahibi sayfaya asla hata fırlatmaz; Shadow DOM içinde
554
+ çalışır, sayfanızın CSS'iyle çakışmaz; < 20 KB gzip. Ayrıntı:
555
+ `docs/CONTRACT.md § 9` ve https://signalbird.io/sdk/widget.
556
+
557
+ ## Hata kodları
558
+
559
+ | Kod | Anlamı |
560
+ |---|---|
561
+ | `INVALID_KEY` | Anahtar yok, yanlış ya da proje pasif |
562
+ | `SECRET_KEY_IN_BROWSER` | Gizli anahtar tarayıcıdan kullanıldı |
563
+ | `ORIGIN_NOT_ALLOWED` | Tarayıcı anahtarı bu alan adına açık değil |
564
+ | `CHANNEL_NOT_ALLOWED` | Tarayıcı anahtarı bu kanala yazamaz |
565
+ | `MODULE_DISABLED` | Paketinizde Telsiz (`logger`) modülü yok |
566
+ | `LIMIT_REACHED` | Aylık kayıt limitiniz doldu |
567
+ | `CHANNEL_DISABLED` | Kanal kapalı — kayıt yazılmaz, kota da harcanmaz |
568
+
569
+ Gönderim ve Yönetim istemcilerine özgü: `WRONG_KEY_TYPE` (kurulumda),
570
+ `API_KEY_INVALID`, `API_KEY_SCOPE` (anahtarda gereken scope yok),
571
+ `VALIDATION_ERROR` (422), `NO_CONSENT`, `SUPPRESSED`, `NO_SENDING_DOMAIN`,
572
+ `LIST_NOT_FOUND`, `MODULE_DISABLED`, `NETWORK_ERROR`, `TIMEOUT`, `HTTP_<durum>`.
573
+
574
+ Uygulama yüzeyi ve widget: `VISITOR_INVALID` (yerel kimlik silinir, yeni
575
+ oturum açılır), `CHAT_UNAVAILABLE` (kota — "sohbet kullanılamıyor" bandı),
576
+ `NOT_INITIALIZED`.
@@ -0,0 +1,53 @@
1
+ import { Observable } from 'rxjs';
2
+ import { SignalbirdApp, AppConfig, ChatState, SessionInput, IdentifyInput, RegisterDeviceInput } from './app.mjs';
3
+
4
+ /**
5
+ * `signalbird/angular` — Angular servisi ve sağlayıcısı.
6
+ *
7
+ * Dekoratör KULLANMAZ: `@Injectable()` yazsaydık paketin derlenmesi Angular
8
+ * sürümüne bağlanırdı ve her büyük sürümde yeniden yayın gerekirdi. Bunun
9
+ * yerine düz sınıf + `provideSignalbird()` fabrikası veriyoruz; Angular'ın DI'ı
10
+ * bunu sorunsuz kabul eder ve sürümden bağımsızdır.
11
+ *
12
+ * RxJS bir `peerDependency`'dir (Angular zaten getirir).
13
+ */
14
+
15
+ /** DI belirteci — Angular'ın `InjectionToken`'ına sarılır (aşağıda). */
16
+ declare const SIGNALBIRD_CONFIG = "SIGNALBIRD_CONFIG";
17
+ /**
18
+ * Uygulama başına tek örnek (`providedIn: 'root'` karşılığı).
19
+ *
20
+ * Sohbet durumu bir `Observable`'dır; şablon `| async` ile doğrudan bağlanır.
21
+ */
22
+ declare class SignalbirdService {
23
+ readonly client: SignalbirdApp;
24
+ private readonly session;
25
+ private readonly subject;
26
+ private started;
27
+ constructor(config: AppConfig);
28
+ /** Sohbet durumu akışı. İlk abone olduğunda oturum başlar. */
29
+ chat$(): Observable<ChatState>;
30
+ /** Panel açık mı — yoklama hızını belirler. */
31
+ setOpen(open: boolean): void;
32
+ send(body: string, attachments?: unknown[]): Promise<unknown>;
33
+ typing(isTyping: boolean): void;
34
+ markRead(): Promise<void>;
35
+ closeConversation(rating?: number, comment?: string): Promise<void>;
36
+ openSession(input: SessionInput): Promise<unknown>;
37
+ identify(input: IdentifyInput): Promise<unknown>;
38
+ registerDevice(input: RegisterDeviceInput): Promise<unknown>;
39
+ /** Uygulama kapanırken ya da testte: yoklamayı durdurur. */
40
+ destroy(): void;
41
+ }
42
+ /**
43
+ * `bootstrapApplication(App, { providers: [provideSignalbird({ appKey })] })`
44
+ *
45
+ * Dönen nesne Angular'ın `Provider` biçimindedir ama tipi buraya gömülmez —
46
+ * paket Angular'a derleme zamanı bağımlılık taşımaz.
47
+ */
48
+ declare function provideSignalbird(config: AppConfig): {
49
+ provide: typeof SignalbirdService;
50
+ useFactory: () => SignalbirdService;
51
+ };
52
+
53
+ export { SIGNALBIRD_CONFIG, SignalbirdService, provideSignalbird };
@@ -0,0 +1,53 @@
1
+ import { Observable } from 'rxjs';
2
+ import { SignalbirdApp, AppConfig, ChatState, SessionInput, IdentifyInput, RegisterDeviceInput } from './app.js';
3
+
4
+ /**
5
+ * `signalbird/angular` — Angular servisi ve sağlayıcısı.
6
+ *
7
+ * Dekoratör KULLANMAZ: `@Injectable()` yazsaydık paketin derlenmesi Angular
8
+ * sürümüne bağlanırdı ve her büyük sürümde yeniden yayın gerekirdi. Bunun
9
+ * yerine düz sınıf + `provideSignalbird()` fabrikası veriyoruz; Angular'ın DI'ı
10
+ * bunu sorunsuz kabul eder ve sürümden bağımsızdır.
11
+ *
12
+ * RxJS bir `peerDependency`'dir (Angular zaten getirir).
13
+ */
14
+
15
+ /** DI belirteci — Angular'ın `InjectionToken`'ına sarılır (aşağıda). */
16
+ declare const SIGNALBIRD_CONFIG = "SIGNALBIRD_CONFIG";
17
+ /**
18
+ * Uygulama başına tek örnek (`providedIn: 'root'` karşılığı).
19
+ *
20
+ * Sohbet durumu bir `Observable`'dır; şablon `| async` ile doğrudan bağlanır.
21
+ */
22
+ declare class SignalbirdService {
23
+ readonly client: SignalbirdApp;
24
+ private readonly session;
25
+ private readonly subject;
26
+ private started;
27
+ constructor(config: AppConfig);
28
+ /** Sohbet durumu akışı. İlk abone olduğunda oturum başlar. */
29
+ chat$(): Observable<ChatState>;
30
+ /** Panel açık mı — yoklama hızını belirler. */
31
+ setOpen(open: boolean): void;
32
+ send(body: string, attachments?: unknown[]): Promise<unknown>;
33
+ typing(isTyping: boolean): void;
34
+ markRead(): Promise<void>;
35
+ closeConversation(rating?: number, comment?: string): Promise<void>;
36
+ openSession(input: SessionInput): Promise<unknown>;
37
+ identify(input: IdentifyInput): Promise<unknown>;
38
+ registerDevice(input: RegisterDeviceInput): Promise<unknown>;
39
+ /** Uygulama kapanırken ya da testte: yoklamayı durdurur. */
40
+ destroy(): void;
41
+ }
42
+ /**
43
+ * `bootstrapApplication(App, { providers: [provideSignalbird({ appKey })] })`
44
+ *
45
+ * Dönen nesne Angular'ın `Provider` biçimindedir ama tipi buraya gömülmez —
46
+ * paket Angular'a derleme zamanı bağımlılık taşımaz.
47
+ */
48
+ declare function provideSignalbird(config: AppConfig): {
49
+ provide: typeof SignalbirdService;
50
+ useFactory: () => SignalbirdService;
51
+ };
52
+
53
+ export { SIGNALBIRD_CONFIG, SignalbirdService, provideSignalbird };