signalbird 2.6.0 → 2.9.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/dist/index.d.ts CHANGED
@@ -57,7 +57,7 @@ interface LogResult {
57
57
  ok: boolean;
58
58
  /** Sunucunun döndüğü olay kimliği (rev_…). */
59
59
  eventId?: string;
60
- /** Reddedildiyse sebep: INVALID_KEY, MODULE_DISABLED, LIMIT_REACHED… */
60
+ /** Reddedildiyse sebep: DOMAIN_KEY_INVALID, MODULE_DISABLED, MODULE_KEY_DISABLED, LIMIT_REACHED… */
61
61
  code?: string;
62
62
  status?: number;
63
63
  }
@@ -114,13 +114,22 @@ declare class SignalbirdClient {
114
114
  error: (message: string, context?: Record<string, unknown>) => Promise<LogResult>;
115
115
  critical: (message: string, context?: Record<string, unknown>) => Promise<LogResult>;
116
116
  };
117
- /** Tek kayıt gönderir. */
117
+ /**
118
+ * Tek kayıt gönderir.
119
+ *
120
+ * `message` 4000 kod noktasına kırpılır (API sınırı; aşan satır 422 alırdı)
121
+ * ve `context` güvenli kopyaya çevrilir: döngüsel nesne ya da BigInt artık
122
+ * `NETWORK_ERROR` gibi görünen bir istisnaya dönüşmez.
123
+ */
118
124
  log(input: LogInput): Promise<LogResult>;
119
125
  /**
120
126
  * Toplu gönderim - 100 kayda kadar.
121
127
  *
122
128
  * Kısmi başarı normaldir (kota tam ortada dolabilir), o yüzden sonuç tek bir
123
129
  * durum değil satır satır döner.
130
+ *
131
+ * Doğrulama ise satır satır DEĞİLDİR: tek bir geçersiz satır (4000'i aşan
132
+ * mesaj) bütün paketi 422 ile düşürür. Bu yüzden kırpma burada da yapılır.
124
133
  */
125
134
  batch(events: LogInput[]): Promise<BatchResult>;
126
135
  debugLog(key: string, message: string, context?: Record<string, unknown>): Promise<LogResult>;
@@ -128,6 +137,17 @@ declare class SignalbirdClient {
128
137
  warn(key: string, message: string, context?: Record<string, unknown>): Promise<LogResult>;
129
138
  error(key: string, message: string, context?: Record<string, unknown>): Promise<LogResult>;
130
139
  critical(key: string, message: string, context?: Record<string, unknown>): Promise<LogResult>;
140
+ /**
141
+ * Kimlik doğrulama hash'i (CONTRACT §15.2) - sohbet/push ziyaretçisinin
142
+ * `external_id`'sini GÜVENİLİR kılar.
143
+ *
144
+ * identity_hash = hex(HMAC-SHA256(hex(SHA-256(secretKey)), externalId))
145
+ *
146
+ * Anahtar gizli anahtarın kendisi değil SHA-256 özetidir: sunucu anahtarı
147
+ * düz saklamaz. Hash sunucuda üretilir, sayfaya `external_id` ile birlikte
148
+ * yazılır; gizli anahtar istemciye İNMEZ.
149
+ */
150
+ identityHash(externalId: string): string;
131
151
  /**
132
152
  * Yakalanmamış hataları Telsiz'e bağlar.
133
153
  *
@@ -148,7 +168,7 @@ declare class SignalbirdClient {
148
168
  * bulunur ve iki doküman arasında çeviri tablosu gerekmez.
149
169
  */
150
170
  interface MessagingConfig {
151
- /** Takım API anahtarı (`sb_…`). GİZLİDİR, yalnız sunucuda kullanılır. */
171
+ /** Gizli domain anahtarı (`sb_secret_live_…`). Yalnız sunucuda kullanılır. */
152
172
  domainKey: string;
153
173
  /** Varsayılan: https://live.signalbird.io/api */
154
174
  baseUrl?: string;
@@ -174,22 +194,96 @@ type SbResult$1<T> = {
174
194
  /** İleti sınıfı - API'de zorunludur ve varsayılanı YOKTUR (hukuki kapı). */
175
195
  type MessageClass = 'transactional' | 'commercial';
176
196
  type Channel = 'email' | 'sms' | 'push';
197
+ /** E-posta eki - içerik base64; toplam çözülmüş boyut sınırı sunucudadır (7 MB). */
198
+ interface EmailAttachment {
199
+ filename: string;
200
+ mime?: string;
201
+ /** Dosya içeriği, base64. */
202
+ content_b64: string;
203
+ }
204
+ /**
205
+ * `POST /v1/email/send` gövdesi - alanlar API doğrulamasıyla birebir
206
+ * (signalbird.api `Api\V1\MessagingController::sendEmail`).
207
+ *
208
+ * İçerik iki yoldan gelir: ya `subject` + `body`, ya panelde tanımlı bir
209
+ * şablon (`template` adıyla ya da `template_id` ile). Şablon verilmediyse
210
+ * `subject` ve `body` ZORUNLUDUR (API: `required_without_all:template_id,template`);
211
+ * verildiyse ikisi de şablondan gelir ve istenirse ezilebilir.
212
+ *
213
+ * Tip bilerek arayüz olarak kaldı (birleşim tipi değil): kullanıcı kodunda
214
+ * `extends SendEmailInput` yazan herkes kırılırdı.
215
+ */
177
216
  interface SendEmailInput {
217
+ /** Alıcı e-posta adresi. */
178
218
  to: string;
219
+ /** Zorunlu, varsayılanı yok: işlemsel ile ticari arasındaki fark hukukidir. */
179
220
  class: MessageClass;
180
- subject: string;
221
+ /** Konu (en fazla 255). Şablon yoksa zorunlu. */
222
+ subject?: string;
223
+ /** Gövde - HTML ya da düz metin. Şablon yoksa zorunlu. */
181
224
  body?: string;
182
- template_hash?: string;
225
+ /** Panelde tanımlı şablonun ADI (en fazla 190). Ad tercih edilir: şablon yeniden yaratılsa da kod değişmez. */
226
+ template?: string;
227
+ /** Panelde tanımlı şablonun kimliği. */
228
+ template_id?: number;
229
+ /** Şablon/gövde değişkenleri - `{{ad}}` yerine geçer. */
183
230
  vars?: Record<string, unknown>;
231
+ /**
232
+ * GÖNDERİCİ KANALI: panelde adresle birlikte açılan `email` modül anahtarı
233
+ * (`noReply`). From adresini kanal seçer - PHP'deki
234
+ * `Signalbird::sendMail('noReply')` karşılığı. Kanala adres bağlanmamışsa
235
+ * 422 `SENDER_NOT_CONFIGURED`; tanımsız kanal `MODULE_KEY_NOT_FOUND`.
236
+ */
237
+ module_key?: string;
238
+ /** Belirli bir doğrulanmış gönderen alan adından çıksın. */
184
239
  sending_domain_id?: number;
240
+ /** Gönderen ADRES seçimi (destek@…); alan adını da belirler. `module_key` verilirse kanalınki geçerlidir. */
241
+ sending_address_id?: number;
242
+ /** Signalbird'deki kişi kaydı - açılma/tıklama geçmişi ona yazılsın. */
185
243
  contact_id?: number;
244
+ /** Görünen gönderen adı (en fazla 120). Zarf adresi değil. */
245
+ from_name?: string;
246
+ /** "Yanıtla" adresi. */
247
+ reply_to?: string;
248
+ /** En fazla 5 ek. */
249
+ attachments?: EmailAttachment[];
250
+ /**
251
+ * @deprecated API bu alanı hiç okumaz ve SDK onu GÖNDERMEZ (2.9.0). Şablon
252
+ * için `template` (ad) ya da `template_id` kullanın. Yalnız eski kodun tip
253
+ * denetiminde kırılmaması için tanımlı; başka bir alana eşlenmez.
254
+ */
255
+ template_hash?: string;
186
256
  }
257
+ /**
258
+ * `POST /v1/sms/send` gövdesi - alanlar API doğrulamasıyla birebir
259
+ * (signalbird.api `Api\V1\MessagingController::sendSms`).
260
+ *
261
+ * İçerik ya `body` ya da panelde tanımlı bir şablondur (`template` adıyla ya
262
+ * da `template_id` ile). Şablon verilmediyse `body` ZORUNLUDUR (API:
263
+ * `required_without_all:template_id,template`).
264
+ *
265
+ * E-postanın aksine SMS ucunda gönderici KANALI (`module_key`) yoktur;
266
+ * gönderen adı `sender` ile seçilir.
267
+ */
187
268
  interface SendSmsInput {
269
+ /** Alıcı telefon (en fazla 20). Sunucu normalize eder; geçersizse 422 `INVALID_PHONE`. */
188
270
  to: string;
271
+ /** Zorunlu, varsayılanı yok: işlemsel ile ticari arasındaki fark hukukidir. */
189
272
  class: MessageClass;
190
- body: string;
273
+ /** Mesaj metni (en fazla 1600). Şablon yoksa zorunlu. */
274
+ body?: string;
275
+ /** Panelde tanımlı şablonun ADI (en fazla 190). */
276
+ template?: string;
277
+ /** Panelde tanımlı şablonun kimliği. */
278
+ template_id?: number;
279
+ /** Şablon/gövde değişkenleri - `{{ad}}` yerine geçer. */
280
+ vars?: Record<string, unknown>;
281
+ /** Marka (SMS başlığı/kotası bu markadan). */
191
282
  brand_id?: number;
283
+ /** Signalbird'deki kişi kaydı. */
192
284
  contact_id?: number;
285
+ /** Onaylı SMS gönderici adı (en fazla 11). Verilmezse şirketin varsayılanı. */
286
+ sender?: string;
193
287
  }
194
288
  interface SendPushInput {
195
289
  /** Cihaz token'ı, `contact:<id>` ya da `external:<external_id>`. */
@@ -599,7 +693,7 @@ interface EmbedToken {
599
693
  }
600
694
 
601
695
  interface ManagementConfig {
602
- /** Takım API anahtarı (`sb_…`) - `radio:*`, `chat:*`, `apps:*` scope'larıyla. */
696
+ /** Gizli domain anahtarı (`sb_secret_live_…`). Scope yoktur (v2); yalnız gömme jetonu ayrı onay ister. */
603
697
  domainKey: string;
604
698
  /** Varsayılan: https://live.signalbird.io/api */
605
699
  baseUrl?: string;
@@ -913,9 +1007,9 @@ interface TeamEmbedTokenInput {
913
1007
  * kayıtlarına dokunur; başka takımın kaydı 404 döner.
914
1008
  *
915
1009
  * Neden ayrı sınıf: Gönderim (`SignalbirdMessaging`) ileti gönderir ve kota
916
- * harcar; bu istemci yapılandırma değiştirir. Aynı anahtar ailesini kullanırlar
917
- * (`sb_…`) ama scope'ları ve hata kümeleri farklıdır - tek sınıfta birleşseydi
918
- * "hangi scope gerekiyordu" sorusu her metotta yeniden sorulurdu.
1010
+ * harcar; bu istemci yapılandırma değiştirir. Aynı gizli domain anahtarını
1011
+ * (`sb_secret_live_…`) kullanırlar ama hata kümeleri farklıdır - tek sınıfta
1012
+ * birleşseydi her metotta "bu kod hangi yüzeyin" sorusu sorulurdu.
919
1013
  *
920
1014
  * Sözleşme: docs/CONTRACT.md § 10
921
1015
  */
@@ -1055,7 +1149,7 @@ declare class SignalbirdManagement {
1055
1149
  *
1056
1150
  * **Bu, CLAUDE.md'deki "Admin yüzeyi OLMAYACAK" kuralının bilinçli
1057
1151
  * istisnasıdır** ve istisna olduğu için ayrı anahtar türü taşır. Kural,
1058
- * müşterinin kendi anahtarıyla (`sb_`) şirket açamaması içindi; o kural aynen
1152
+ * müşterinin kendi anahtarıyla şirket açamaması içindi; o kural aynen
1059
1153
  * duruyor. Sözleşmeli partner farklı bir taraftır.
1060
1154
  *
1061
1155
  * Gizli anahtar **asla tarayıcıya inmez**: gömme jetonunu partner'ın
@@ -1162,11 +1256,12 @@ declare function verifyWebhook(rawBody: string | Uint8Array, signatureHeader: st
1162
1256
  * Node betikleri buradan alır. TARAYICI için `signalbird/browser`
1163
1257
  * kullanılır - gizli anahtar istemciye inmez.
1164
1258
  *
1165
- * Üç sunucu istemcisi vardır; anahtarları ve kapıları farklıdır:
1166
- * - `SignalbirdClient` → Telsiz (log yazma), `sb_secret_live_…`
1167
- * - `SignalbirdMessaging` → Gönderim (e-posta/SMS/push/kişi/kampanya), `sb_…`
1168
- * - `SignalbirdManagement` → Yönetim (Telsiz projesi, sohbet gelen kutusu,
1169
- * uygulama kaydı), `sb_…` + `radio|chat|apps` scope'ları
1259
+ * Sunucu istemcilerinin HEPSİ aynı gizli domain anahtarını (`sb_secret_live_…`)
1260
+ * kullanır; ayrım anahtarda değil, kapıdadır:
1261
+ * - `SignalbirdClient` → Telsiz (log yazma)
1262
+ * - `SignalbirdMessaging` → Gönderim (e-posta/SMS/push/kişi/kampanya)
1263
+ * - `SignalbirdManagement` → Yönetim (olay akışı, modül anahtarları, sohbet
1264
+ * gelen kutusu)
1170
1265
  * - `SignalbirdPartner` → Partner (müşteri sağlama, modül yetkisi, gömme),
1171
1266
  * gizli anahtar - yalnız sözleşmeli platformlar
1172
1267
  *
@@ -1190,8 +1285,8 @@ declare function resetSignalbird(): void;
1190
1285
  /**
1191
1286
  * Ortam değişkeninden kurulan paylaşımlı yönetim istemcisi.
1192
1287
  *
1193
- * `SIGNALBIRD_DOMAIN_KEY` okunur (yoksa `SIGNALBIRD_DOMAIN_KEY` - ikisi de aynı
1194
- * takım anahtarı ailesidir ve çoğu kurulumda tek anahtar kullanılır).
1288
+ * `SIGNALBIRD_DOMAIN_KEY` okunur - Telsiz ve Gönderim ile aynı gizli domain
1289
+ * anahtarı (`sb_secret_live_…`).
1195
1290
  *
1196
1291
  * import { management } from 'signalbird'
1197
1292
  * await management().createModuleKey('logger', { title: 'Kritik API hatası' })
@@ -1200,4 +1295,4 @@ declare function management(config?: Partial<ManagementConfig>): SignalbirdManag
1200
1295
  /** Test ve sıcak yeniden yükleme için yönetim istemcisini sıfırlar. */
1201
1296
  declare function resetManagement(): void;
1202
1297
 
1203
- export { type AddDomainInput, type AddDomainResult, type AppDevice, type AppPlatform, type Batch, type BatchResult, type BulkContactsInput, type BulkContactsResult, type CampaignCreateResult, type CampaignDetail, type CannedReply, type CannedReplyInput, type Channel, type ChatConversation, type ChatMessage, type ChatVisitor, type Contact, type ContactInput, type ContactList, type ConversationStatus, type CreateCampaignInput, type CreateCompanyInput, type CreateCompanyResult, type CreateContactListInput, DEFAULT_BASE_URL, type DnsRecord, type EmbedModule, type EmbedToken, type EmbedTokenInput, type GrantModuleInput, type Level, type ListAppDevicesQuery, type ListCampaignMessagesQuery, type ListCampaignsQuery, type ListChatMessagesQuery, type ListContactsQuery, type ListConversationsQuery, type ListMessagesQuery, type ListRadioEventsQuery, type LogInput, type LogResult, type ManagementConfig, type Message, type MessageClass, type MessagingConfig, type MessagingErrorCode, type ModuleEntitlement, type Paginated$1 as Paginated, type PartnerCompany, type PartnerConfig, type PartnerDomain, type PartnerOwnerInput, type PartnerUser, type PartnerUserInput, type RadioEvent, type RadioLevel, type ReplyInput, type SbResult$1 as SbResult, type SendEmailInput, type SendPushInput, type SendResult, type SendSmsInput, SignalbirdClient, type SignalbirdConfig, SignalbirdError, SignalbirdManagement, SignalbirdMessaging, SignalbirdPartner, type SmsPreview, type StartConversationInput, type TeamEmbedTokenInput, type UpdateConversationInput, type UpdateVisitorInput, type UptimeIncident, type UptimeRange, type UptimeReport, type VerifyDomainResult, management, resetManagement, resetSignalbird, signalbird, verifyWebhook };
1298
+ export { type AddDomainInput, type AddDomainResult, type AppDevice, type AppPlatform, type Batch, type BatchResult, type BulkContactsInput, type BulkContactsResult, type CampaignCreateResult, type CampaignDetail, type CannedReply, type CannedReplyInput, type Channel, type ChatConversation, type ChatMessage, type ChatVisitor, type Contact, type ContactInput, type ContactList, type ConversationStatus, type CreateCampaignInput, type CreateCompanyInput, type CreateCompanyResult, type CreateContactListInput, DEFAULT_BASE_URL, type DnsRecord, type EmailAttachment, type EmbedModule, type EmbedToken, type EmbedTokenInput, type GrantModuleInput, type Level, type ListAppDevicesQuery, type ListCampaignMessagesQuery, type ListCampaignsQuery, type ListChatMessagesQuery, type ListContactsQuery, type ListConversationsQuery, type ListMessagesQuery, type ListRadioEventsQuery, type LogInput, type LogResult, type ManagementConfig, type Message, type MessageClass, type MessagingConfig, type MessagingErrorCode, type ModuleEntitlement, type Paginated$1 as Paginated, type PartnerCompany, type PartnerConfig, type PartnerDomain, type PartnerOwnerInput, type PartnerUser, type PartnerUserInput, type RadioEvent, type RadioLevel, type ReplyInput, type SbResult$1 as SbResult, type SendEmailInput, type SendPushInput, type SendResult, type SendSmsInput, SignalbirdClient, type SignalbirdConfig, SignalbirdError, SignalbirdManagement, SignalbirdMessaging, SignalbirdPartner, type SmsPreview, type StartConversationInput, type TeamEmbedTokenInput, type UpdateConversationInput, type UpdateVisitorInput, type UptimeIncident, type UptimeRange, type UptimeReport, type VerifyDomainResult, management, resetManagement, resetSignalbird, signalbird, verifyWebhook };
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ var SignalbirdError = class extends Error {
17
17
  var DEFAULT_BASE_URL = "https://live.signalbird.io/api";
18
18
 
19
19
  // src/shared/version.ts
20
- var SDK_VERSION = "2.6.0" ;
20
+ var SDK_VERSION = "2.9.0" ;
21
21
  var SDK_HEADER = "X-Signalbird-Sdk";
22
22
  function sdkHeaderValue(platform) {
23
23
  return `${platform}/${SDK_VERSION}`;
@@ -36,7 +36,69 @@ function noteSdkStatus(headers) {
36
36
  }
37
37
  }
38
38
 
39
- // src/node/client.ts
39
+ // src/shared/serialize.ts
40
+ var MAX_MESSAGE_LENGTH = 4e3;
41
+ function truncateMessage(message) {
42
+ const text = typeof message === "string" ? message : String(message);
43
+ if (text.length <= MAX_MESSAGE_LENGTH) {
44
+ return text;
45
+ }
46
+ return Array.from(text).slice(0, MAX_MESSAGE_LENGTH).join("");
47
+ }
48
+ function toJsonSafe(value) {
49
+ const ancestors = [];
50
+ const walk = (current) => {
51
+ if (typeof current === "bigint") {
52
+ return current.toString();
53
+ }
54
+ if (current === null || typeof current !== "object") {
55
+ return current;
56
+ }
57
+ if (ancestors.includes(current)) {
58
+ return "[Circular]";
59
+ }
60
+ if (current instanceof Error) {
61
+ return { name: current.name, message: current.message, stack: current.stack };
62
+ }
63
+ const withToJson = current;
64
+ if (typeof withToJson.toJSON === "function") {
65
+ try {
66
+ ancestors.push(current);
67
+ return walk(withToJson.toJSON());
68
+ } catch {
69
+ return "[Unserializable]";
70
+ } finally {
71
+ ancestors.pop();
72
+ }
73
+ }
74
+ ancestors.push(current);
75
+ try {
76
+ if (Array.isArray(current)) {
77
+ return current.map((item) => walk(item));
78
+ }
79
+ const out = {};
80
+ for (const key of Object.keys(current)) {
81
+ let item;
82
+ try {
83
+ item = current[key];
84
+ } catch {
85
+ item = "[Unserializable]";
86
+ }
87
+ out[key] = walk(item);
88
+ }
89
+ return out;
90
+ } finally {
91
+ ancestors.pop();
92
+ }
93
+ };
94
+ return walk(value);
95
+ }
96
+ function safeContext(context) {
97
+ if (context === void 0 || context === null) {
98
+ return void 0;
99
+ }
100
+ return toJsonSafe(context);
101
+ }
40
102
  var SignalbirdClient = class {
41
103
  constructor(config) {
42
104
  this.config = config;
@@ -81,13 +143,19 @@ var SignalbirdClient = class {
81
143
  critical: (message, context) => this.critical(key, message, context)
82
144
  };
83
145
  }
84
- /** Tek kayıt gönderir. */
146
+ /**
147
+ * Tek kayıt gönderir.
148
+ *
149
+ * `message` 4000 kod noktasına kırpılır (API sınırı; aşan satır 422 alırdı)
150
+ * ve `context` güvenli kopyaya çevrilir: döngüsel nesne ya da BigInt artık
151
+ * `NETWORK_ERROR` gibi görünen bir istisnaya dönüşmez.
152
+ */
85
153
  async log(input) {
86
154
  return this.send("/v1/radio/log", {
87
155
  key: input.key,
88
- message: input.message,
156
+ message: truncateMessage(input.message),
89
157
  level: input.level,
90
- context: input.context,
158
+ context: safeContext(input.context),
91
159
  source: input.source ?? this.source
92
160
  });
93
161
  }
@@ -96,14 +164,17 @@ var SignalbirdClient = class {
96
164
  *
97
165
  * Kısmi başarı normaldir (kota tam ortada dolabilir), o yüzden sonuç tek bir
98
166
  * durum değil satır satır döner.
167
+ *
168
+ * Doğrulama ise satır satır DEĞİLDİR: tek bir geçersiz satır (4000'i aşan
169
+ * mesaj) bütün paketi 422 ile düşürür. Bu yüzden kırpma burada da yapılır.
99
170
  */
100
171
  async batch(events) {
101
172
  const payload = {
102
173
  events: events.slice(0, 100).map((event) => ({
103
174
  key: event.key,
104
- message: event.message,
175
+ message: truncateMessage(event.message),
105
176
  level: event.level,
106
- context: event.context,
177
+ context: safeContext(event.context),
107
178
  source: event.source ?? this.source
108
179
  }))
109
180
  };
@@ -140,6 +211,20 @@ var SignalbirdClient = class {
140
211
  critical(key, message, context) {
141
212
  return this.log({ key, message, level: "critical", context });
142
213
  }
214
+ /**
215
+ * Kimlik doğrulama hash'i (CONTRACT §15.2) - sohbet/push ziyaretçisinin
216
+ * `external_id`'sini GÜVENİLİR kılar.
217
+ *
218
+ * identity_hash = hex(HMAC-SHA256(hex(SHA-256(secretKey)), externalId))
219
+ *
220
+ * Anahtar gizli anahtarın kendisi değil SHA-256 özetidir: sunucu anahtarı
221
+ * düz saklamaz. Hash sunucuda üretilir, sayfaya `external_id` ile birlikte
222
+ * yazılır; gizli anahtar istemciye İNMEZ.
223
+ */
224
+ identityHash(externalId) {
225
+ const key = crypto.createHash("sha256").update(this.config.domainKey).digest("hex");
226
+ return crypto.createHmac("sha256", key).update(String(externalId).trim()).digest("hex");
227
+ }
143
228
  /**
144
229
  * Yakalanmamış hataları Telsiz'e bağlar.
145
230
  *
@@ -176,7 +261,7 @@ var SignalbirdClient = class {
176
261
  if (!response) {
177
262
  return { ok: false, code: "NETWORK_ERROR" };
178
263
  }
179
- if (!response.ok) {
264
+ if (!response.ok || response.body?.ok === false) {
180
265
  const code = response.body?.code ?? "UNKNOWN";
181
266
  if (this.throwOnError) {
182
267
  throw new SignalbirdError(`Signalbird: ${code}`, response.status, code);
@@ -248,7 +333,11 @@ var SignalbirdMessaging = class {
248
333
  }
249
334
  // ── Gönderim ──────────────────────────────────────────────────────────
250
335
  sendEmail(input) {
251
- return this.request("POST", "/v1/email/send", input);
336
+ const { template_hash: _ignored, ...body } = input;
337
+ if (_ignored !== void 0 && this.debug) {
338
+ console.warn("[signalbird] sendEmail: template_hash kullan\u0131mdan kalkt\u0131 ve g\xF6nderilmiyor; template ya da template_id kullan\u0131n.");
339
+ }
340
+ return this.request("POST", "/v1/email/send", body);
252
341
  }
253
342
  sendSms(input) {
254
343
  return this.request("POST", "/v1/sms/send", input);