nobetci-eczane-api 1.0.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.
@@ -0,0 +1,321 @@
1
+ /**
2
+ * Nöbetçi Eczane API TypeScript Types & Interfaces
3
+ */
4
+ /**
5
+ * Coğrafi koordinat nesnesi
6
+ */
7
+ interface Coordinates {
8
+ lat: number;
9
+ lng: number;
10
+ }
11
+ /**
12
+ * Eczane veri modeli (Nöbetçi ve Standart)
13
+ */
14
+ interface Pharmacy {
15
+ id: number;
16
+ city_id: number;
17
+ district_id: number;
18
+ name: string;
19
+ phone: string;
20
+ city: string;
21
+ district: string;
22
+ locality?: string;
23
+ address: string;
24
+ address_description?: string;
25
+ sentry_date?: string;
26
+ is_sentry: boolean;
27
+ workingHours?: string;
28
+ note?: string;
29
+ status?: string;
30
+ coordinates: Coordinates;
31
+ map_link: string;
32
+ /** Konum bazlı aramalarda kullanıcıya olan mesafe (km cinsinden) */
33
+ distance?: number;
34
+ }
35
+ /**
36
+ * Sayfalama (Pagination) meta nesnesi
37
+ */
38
+ interface PaginationMeta {
39
+ total: number;
40
+ per_page: number;
41
+ current_page: number;
42
+ total_pages: number;
43
+ has_more: boolean;
44
+ prev_page: number | null;
45
+ next_page: number | null;
46
+ }
47
+ /**
48
+ * Standart API liste yanıtı
49
+ */
50
+ interface ApiResponse<T = Pharmacy[]> {
51
+ status?: string;
52
+ city_id?: number;
53
+ district_id?: number;
54
+ data: T;
55
+ pagination?: PaginationMeta;
56
+ }
57
+ /**
58
+ * İl Veri Modeli
59
+ */
60
+ interface City {
61
+ id: number;
62
+ name: string;
63
+ slug: string;
64
+ plate: string | number;
65
+ }
66
+ /**
67
+ * İlçe Veri Modeli
68
+ */
69
+ interface District {
70
+ id: number;
71
+ city_id: number;
72
+ name: string;
73
+ slug: string;
74
+ }
75
+ /**
76
+ * API Hesap & Kota Durumu Modeli
77
+ */
78
+ interface AccountInfo {
79
+ api_key: string;
80
+ status: string;
81
+ registered_at?: string;
82
+ expires_at?: string;
83
+ remaining_days?: number;
84
+ allowed_ips?: string[];
85
+ rate_limit?: {
86
+ limit: number;
87
+ remaining: number;
88
+ reset_in_seconds?: number;
89
+ };
90
+ }
91
+ /**
92
+ * IP Whitelist Güncelleme Yanıt Modeli
93
+ */
94
+ interface WhitelistUpdateResponse {
95
+ status: 'success' | 'error';
96
+ message: string;
97
+ allowed_ips?: string[];
98
+ }
99
+ /**
100
+ * Sayfalama ve limit parametreleri
101
+ */
102
+ interface PaginationOptions {
103
+ /** Sayfa numarası (varsayılan: 1) */
104
+ page?: number;
105
+ /** Sayfa başına kayıt sayısı (1 - 50, varsayılan: 25) */
106
+ limit?: number;
107
+ /** Özel AbortSignal (istek iptali için) */
108
+ signal?: AbortSignal;
109
+ }
110
+ /**
111
+ * Konum bazlı en yakın eczane arama parametreleri
112
+ */
113
+ interface NearbyOptions {
114
+ /** Kullanıcı enlemi (Latitude) */
115
+ lat: number;
116
+ /** Kullanıcı boylamı (Longitude) */
117
+ lon: number;
118
+ /** Yalnızca nöbetçi eczaneler mi aransın? (varsayılan: true) */
119
+ isSentry?: boolean;
120
+ /** Maksimum döndürülecek eczane sayısı (varsayılan: 10, maks: 50) */
121
+ limit?: number;
122
+ /** Arama yarıçapı kilometre cinsinden (opsiyonel) */
123
+ radius?: number;
124
+ /** Özel AbortSignal (istek iptali için) */
125
+ signal?: AbortSignal;
126
+ }
127
+ /**
128
+ * SDK Yapılandırma Seçenekleri
129
+ */
130
+ interface EczaneAPIOptions {
131
+ /** Eczaneler.ORG v2 REST API Anahtarı */
132
+ apiKey: string;
133
+ /** Özel API Base URL (Varsayılan: https://eczaneler.org/api/v2) */
134
+ baseUrl?: string;
135
+ /** İstek zaman aşımı süresi milisaniye cinsinden (varsayılan: 10000ms - 10s) */
136
+ timeout?: number;
137
+ /** Özel HTTP istek başlıkları */
138
+ headers?: Record<string, string>;
139
+ }
140
+
141
+ /**
142
+ * Eczaneler.ORG v2 REST API İstemcisi
143
+ */
144
+ declare class EczaneAPI {
145
+ private readonly apiKey;
146
+ private readonly baseUrl;
147
+ private readonly timeout;
148
+ private readonly customHeaders;
149
+ /**
150
+ * Yeni bir EczaneAPI istemcisi örneği oluşturur
151
+ * @param options Yapılandırma ayarları
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * import { EczaneAPI } from 'nobetci-eczane-api';
156
+ *
157
+ * const api = new EczaneAPI({
158
+ * apiKey: 'SENIN_API_ANAHTARIN'
159
+ * });
160
+ * ```
161
+ */
162
+ constructor(options: EczaneAPIOptions | string);
163
+ /**
164
+ * Türkiye genelinde o gün nöbetçi olan tüm eczaneleri listeler
165
+ *
166
+ * @param options Sayfalama ve limit ayarları (?page=1&limit=25)
167
+ * @returns Sayfalanmış nöbetçi eczane listesi
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * const result = await api.getSentryPharmacies({ page: 1, limit: 50 });
172
+ * console.log(`Toplam ${result.pagination?.total} nöbetçi eczane bulundu.`);
173
+ * ```
174
+ */
175
+ getSentryPharmacies(options?: PaginationOptions): Promise<ApiResponse<Pharmacy[]>>;
176
+ /**
177
+ * Belirtilen ildeki nöbetçi eczaneleri listeler
178
+ *
179
+ * @param city İl adı / slug'ı (örn: "istanbul", "ankara") VEYA Plaka/Şehir ID (örn: 34, 6)
180
+ * @param options Sayfalama ve limit ayarları
181
+ * @returns İldeki nöbetçi eczane listesi
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * // Şehir ID (34 - İstanbul) ile nöbetçileri çekme
186
+ * const result = await api.getSentryByCity(34, { limit: 50 });
187
+ *
188
+ * // Slug ile çekme
189
+ * const izmir = await api.getSentryByCity('izmir');
190
+ * ```
191
+ */
192
+ getSentryByCity(city: string | number, options?: PaginationOptions): Promise<ApiResponse<Pharmacy[]>>;
193
+ /**
194
+ * Belirtilen ilçedeki nöbetçi eczaneleri listeler
195
+ *
196
+ * @param city İl slug veya ID (örn: "istanbul" veya 34)
197
+ * @param district İlçe slug veya ID (örn: "kadikoy" veya 440)
198
+ * @param options Sayfalama ve limit ayarları
199
+ * @returns İlçedeki nöbetçi eczane listesi
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * const result = await api.getSentryByDistrict(34, 440);
204
+ * // Veya slug ile:
205
+ * const result2 = await api.getSentryByDistrict('istanbul', 'kadikoy');
206
+ * ```
207
+ */
208
+ getSentryByDistrict(city: string | number, district: string | number, options?: PaginationOptions): Promise<ApiResponse<Pharmacy[]>>;
209
+ /**
210
+ * Coğrafi koordinatlara (enlem / boylam) göre en yakın eczaneleri mesafeye göre sıralı getirir
211
+ *
212
+ * @param options Enlem (lat), boylam (lon), isSentry ve limit ayarları
213
+ * @returns En yakından uzağa sıralanmış eczane listesi
214
+ *
215
+ * @example
216
+ * ```typescript
217
+ * const result = await api.getNearby({
218
+ * lat: 41.0082,
219
+ * lon: 28.9784,
220
+ * isSentry: true,
221
+ * limit: 10
222
+ * });
223
+ * ```
224
+ */
225
+ getNearby(options: NearbyOptions): Promise<ApiResponse<Pharmacy[]>>;
226
+ /**
227
+ * Desteklenen tüm il listesini döner
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * const { data: cities } = await api.getCities();
232
+ * ```
233
+ */
234
+ getCities(signal?: AbortSignal): Promise<ApiResponse<City[]>>;
235
+ /**
236
+ * Belirtilen ile ait ilçe listesini döner
237
+ *
238
+ * @param cityId İl ID (örn: 34 - İstanbul)
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * const { data: districts } = await api.getDistricts(34);
243
+ * ```
244
+ */
245
+ getDistricts(cityId: number, signal?: AbortSignal): Promise<ApiResponse<District[]>>;
246
+ /**
247
+ * API anahtarınıza ait kalan kullanım, kota ve yetki bilgilerini döner
248
+ *
249
+ * @example
250
+ * ```typescript
251
+ * const account = await api.getAccountInfo();
252
+ * console.log(`Kalan süre: ${account.remaining_days} gün`);
253
+ * ```
254
+ */
255
+ getAccountInfo(signal?: AbortSignal): Promise<AccountInfo>;
256
+ /**
257
+ * Dinamik IP Whitelist tanımını günceller
258
+ *
259
+ * @param ips İzin verilecek IP adresi veya maskesi (örn: "5.132.*" veya "195.175.20.10")
260
+ *
261
+ * @example
262
+ * ```typescript
263
+ * const res = await api.updateWhitelist('5.132.*');
264
+ * ```
265
+ */
266
+ updateWhitelist(ips: string, signal?: AbortSignal): Promise<WhitelistUpdateResponse>;
267
+ /**
268
+ * Türkiye'deki tüm eczaneleri (nöbetçi olmayanlar dahil +30.000) sayfalı olarak döner (Premium Servis)
269
+ *
270
+ * @param options Sayfalama ve limit ayarları
271
+ *
272
+ * @example
273
+ * ```typescript
274
+ * const result = await api.getAllPharmacies({ page: 1, limit: 50 });
275
+ * ```
276
+ */
277
+ getAllPharmacies(options?: PaginationOptions): Promise<ApiResponse<Pharmacy[]>>;
278
+ /**
279
+ * Merkezi HTTP İstek Yürütücüsü (Native fetch)
280
+ */
281
+ private request;
282
+ /**
283
+ * HTTP Hata Durumlarını Özelleştirilmiş Hata Sınıflarına Çevirir
284
+ */
285
+ private handleHttpError;
286
+ /**
287
+ * Sayfalama query string oluşturucu
288
+ */
289
+ private buildPaginationQuery;
290
+ }
291
+
292
+ /**
293
+ * Nöbetçi Eczane API Özel Hata Sınıfları
294
+ */
295
+ declare class EczaneAPIError extends Error {
296
+ readonly status: number;
297
+ readonly code?: string;
298
+ readonly details?: unknown;
299
+ constructor(message: string, status?: number, code?: string, details?: unknown);
300
+ }
301
+ declare class AuthenticationError extends EczaneAPIError {
302
+ constructor(message?: string);
303
+ }
304
+ declare class ForbiddenError extends EczaneAPIError {
305
+ constructor(message?: string);
306
+ }
307
+ declare class NotFoundError extends EczaneAPIError {
308
+ constructor(message?: string);
309
+ }
310
+ declare class RateLimitError extends EczaneAPIError {
311
+ readonly resetInSeconds?: number;
312
+ constructor(message?: string, resetInSeconds?: number);
313
+ }
314
+ declare class InvalidRequestError extends EczaneAPIError {
315
+ constructor(message?: string, details?: unknown);
316
+ }
317
+ declare class TimeoutError extends EczaneAPIError {
318
+ constructor(message?: string);
319
+ }
320
+
321
+ export { type AccountInfo, type ApiResponse, AuthenticationError, type City, type Coordinates, type District, EczaneAPI, EczaneAPIError, type EczaneAPIOptions, ForbiddenError, InvalidRequestError, type NearbyOptions, NotFoundError, type PaginationMeta, type PaginationOptions, type Pharmacy, RateLimitError, TimeoutError, type WhitelistUpdateResponse, EczaneAPI as default };
package/dist/index.js ADDED
@@ -0,0 +1,391 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ // src/errors.ts
6
+ var EczaneAPIError = class extends Error {
7
+ status;
8
+ code;
9
+ details;
10
+ constructor(message, status = 500, code, details) {
11
+ super(message);
12
+ this.name = "EczaneAPIError";
13
+ this.status = status;
14
+ this.code = code;
15
+ this.details = details;
16
+ Object.setPrototypeOf(this, new.target.prototype);
17
+ }
18
+ };
19
+ var AuthenticationError = class extends EczaneAPIError {
20
+ constructor(message = "Ge\xE7ersiz veya eksik API anahtar\u0131 (X-Api-Key).") {
21
+ super(message, 401, "UNAUTHORIZED");
22
+ this.name = "AuthenticationError";
23
+ }
24
+ };
25
+ var ForbiddenError = class extends EczaneAPIError {
26
+ constructor(message = "Eri\u015Fim engellendi. IP adresiniz whitelist listesinde olmayabilir veya yetkisiz istek.") {
27
+ super(message, 403, "FORBIDDEN");
28
+ this.name = "ForbiddenError";
29
+ }
30
+ };
31
+ var NotFoundError = class extends EczaneAPIError {
32
+ constructor(message = "\u0130stenen kaynak veya eczane kayd\u0131 bulunamad\u0131.") {
33
+ super(message, 404, "NOT_FOUND");
34
+ this.name = "NotFoundError";
35
+ }
36
+ };
37
+ var RateLimitError = class extends EczaneAPIError {
38
+ resetInSeconds;
39
+ constructor(message = "API istek limiti a\u015F\u0131ld\u0131. L\xFCtfen bir s\xFCre sonra tekrar deneyiniz.", resetInSeconds) {
40
+ super(message, 429, "RATE_LIMIT_EXCEEDED");
41
+ this.name = "RateLimitError";
42
+ this.resetInSeconds = resetInSeconds;
43
+ }
44
+ };
45
+ var InvalidRequestError = class extends EczaneAPIError {
46
+ constructor(message = "Ge\xE7ersiz istek parametreleri veya koordinat verisi.", details) {
47
+ super(message, 400, "BAD_REQUEST", details);
48
+ this.name = "InvalidRequestError";
49
+ }
50
+ };
51
+ var TimeoutError = class extends EczaneAPIError {
52
+ constructor(message = "\u0130stek zaman a\u015F\u0131m\u0131na u\u011Frad\u0131.") {
53
+ super(message, 408, "REQUEST_TIMEOUT");
54
+ this.name = "TimeoutError";
55
+ }
56
+ };
57
+
58
+ // src/client.ts
59
+ var DEFAULT_BASE_URL = "https://eczaneler.org/api/v2";
60
+ var DEFAULT_TIMEOUT = 1e4;
61
+ var EczaneAPI = class {
62
+ apiKey;
63
+ baseUrl;
64
+ timeout;
65
+ customHeaders;
66
+ /**
67
+ * Yeni bir EczaneAPI istemcisi örneği oluşturur
68
+ * @param options Yapılandırma ayarları
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * import { EczaneAPI } from 'nobetci-eczane-api';
73
+ *
74
+ * const api = new EczaneAPI({
75
+ * apiKey: 'SENIN_API_ANAHTARIN'
76
+ * });
77
+ * ```
78
+ */
79
+ constructor(options) {
80
+ if (typeof options === "string") {
81
+ this.apiKey = options;
82
+ this.baseUrl = DEFAULT_BASE_URL;
83
+ this.timeout = DEFAULT_TIMEOUT;
84
+ this.customHeaders = {};
85
+ } else {
86
+ if (!options?.apiKey) {
87
+ throw new AuthenticationError("API anahtar\u0131 (apiKey) zorunludur.");
88
+ }
89
+ this.apiKey = options.apiKey;
90
+ this.baseUrl = (options.baseUrl || DEFAULT_BASE_URL).replace(/\/+$/, "");
91
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
92
+ this.customHeaders = options.headers || {};
93
+ }
94
+ }
95
+ /**
96
+ * Türkiye genelinde o gün nöbetçi olan tüm eczaneleri listeler
97
+ *
98
+ * @param options Sayfalama ve limit ayarları (?page=1&limit=25)
99
+ * @returns Sayfalanmış nöbetçi eczane listesi
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * const result = await api.getSentryPharmacies({ page: 1, limit: 50 });
104
+ * console.log(`Toplam ${result.pagination?.total} nöbetçi eczane bulundu.`);
105
+ * ```
106
+ */
107
+ async getSentryPharmacies(options) {
108
+ const query = this.buildPaginationQuery(options);
109
+ return this.request(`/pharmacies/sentry-pharmacies${query}`, {
110
+ method: "GET",
111
+ signal: options?.signal
112
+ });
113
+ }
114
+ /**
115
+ * Belirtilen ildeki nöbetçi eczaneleri listeler
116
+ *
117
+ * @param city İl adı / slug'ı (örn: "istanbul", "ankara") VEYA Plaka/Şehir ID (örn: 34, 6)
118
+ * @param options Sayfalama ve limit ayarları
119
+ * @returns İldeki nöbetçi eczane listesi
120
+ *
121
+ * @example
122
+ * ```typescript
123
+ * // Şehir ID (34 - İstanbul) ile nöbetçileri çekme
124
+ * const result = await api.getSentryByCity(34, { limit: 50 });
125
+ *
126
+ * // Slug ile çekme
127
+ * const izmir = await api.getSentryByCity('izmir');
128
+ * ```
129
+ */
130
+ async getSentryByCity(city, options) {
131
+ if (!city) {
132
+ throw new InvalidRequestError("\u015Eehir bilgisi (city slug veya ID) zorunludur.");
133
+ }
134
+ const query = this.buildPaginationQuery(options);
135
+ return this.request(`/pharmacies/sentry-city-list/${encodeURIComponent(city)}${query}`, {
136
+ method: "GET",
137
+ signal: options?.signal
138
+ });
139
+ }
140
+ /**
141
+ * Belirtilen ilçedeki nöbetçi eczaneleri listeler
142
+ *
143
+ * @param city İl slug veya ID (örn: "istanbul" veya 34)
144
+ * @param district İlçe slug veya ID (örn: "kadikoy" veya 440)
145
+ * @param options Sayfalama ve limit ayarları
146
+ * @returns İlçedeki nöbetçi eczane listesi
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * const result = await api.getSentryByDistrict(34, 440);
151
+ * // Veya slug ile:
152
+ * const result2 = await api.getSentryByDistrict('istanbul', 'kadikoy');
153
+ * ```
154
+ */
155
+ async getSentryByDistrict(city, district, options) {
156
+ if (!city || !district) {
157
+ throw new InvalidRequestError("\u0130l ve il\xE7e parametreleri zorunludur.");
158
+ }
159
+ const query = this.buildPaginationQuery(options);
160
+ return this.request(
161
+ `/pharmacies/sentry-district-list/${encodeURIComponent(city)}/${encodeURIComponent(district)}${query}`,
162
+ {
163
+ method: "GET",
164
+ signal: options?.signal
165
+ }
166
+ );
167
+ }
168
+ /**
169
+ * Coğrafi koordinatlara (enlem / boylam) göre en yakın eczaneleri mesafeye göre sıralı getirir
170
+ *
171
+ * @param options Enlem (lat), boylam (lon), isSentry ve limit ayarları
172
+ * @returns En yakından uzağa sıralanmış eczane listesi
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * const result = await api.getNearby({
177
+ * lat: 41.0082,
178
+ * lon: 28.9784,
179
+ * isSentry: true,
180
+ * limit: 10
181
+ * });
182
+ * ```
183
+ */
184
+ async getNearby(options) {
185
+ if (typeof options?.lat !== "number" || typeof options?.lon !== "number") {
186
+ throw new InvalidRequestError("Enlem (lat) ve boylam (lon) say\u0131sal de\u011Fer olarak girilmelidir.");
187
+ }
188
+ const payload = {
189
+ lat: options.lat,
190
+ lon: options.lon,
191
+ is_sentry: options.isSentry === false ? 0 : 1,
192
+ limit: options.limit ? Math.min(Math.max(1, options.limit), 50) : 10,
193
+ ...options.radius ? { radius: options.radius } : {}
194
+ };
195
+ return this.request("/pharmacies/pharmacies-nearby", {
196
+ method: "POST",
197
+ body: JSON.stringify(payload),
198
+ headers: {
199
+ "Content-Type": "application/json"
200
+ },
201
+ signal: options?.signal
202
+ });
203
+ }
204
+ /**
205
+ * Desteklenen tüm il listesini döner
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * const { data: cities } = await api.getCities();
210
+ * ```
211
+ */
212
+ async getCities(signal) {
213
+ return this.request("/pharmacies/cities", {
214
+ method: "GET",
215
+ signal
216
+ });
217
+ }
218
+ /**
219
+ * Belirtilen ile ait ilçe listesini döner
220
+ *
221
+ * @param cityId İl ID (örn: 34 - İstanbul)
222
+ *
223
+ * @example
224
+ * ```typescript
225
+ * const { data: districts } = await api.getDistricts(34);
226
+ * ```
227
+ */
228
+ async getDistricts(cityId, signal) {
229
+ if (!cityId) {
230
+ throw new InvalidRequestError("Ge\xE7erli bir cityId belirtilmelidir.");
231
+ }
232
+ return this.request(`/pharmacies/districts?city_id=${encodeURIComponent(cityId)}`, {
233
+ method: "GET",
234
+ signal
235
+ });
236
+ }
237
+ /**
238
+ * API anahtarınıza ait kalan kullanım, kota ve yetki bilgilerini döner
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * const account = await api.getAccountInfo();
243
+ * console.log(`Kalan süre: ${account.remaining_days} gün`);
244
+ * ```
245
+ */
246
+ async getAccountInfo(signal) {
247
+ return this.request("/pharmacies/account-info", {
248
+ method: "GET",
249
+ signal
250
+ });
251
+ }
252
+ /**
253
+ * Dinamik IP Whitelist tanımını günceller
254
+ *
255
+ * @param ips İzin verilecek IP adresi veya maskesi (örn: "5.132.*" veya "195.175.20.10")
256
+ *
257
+ * @example
258
+ * ```typescript
259
+ * const res = await api.updateWhitelist('5.132.*');
260
+ * ```
261
+ */
262
+ async updateWhitelist(ips, signal) {
263
+ if (!ips || typeof ips !== "string") {
264
+ throw new InvalidRequestError("Ge\xE7erli bir IP adresi veya deseni (ips) girilmelidir.");
265
+ }
266
+ return this.request("/update-whitelist", {
267
+ method: "POST",
268
+ body: JSON.stringify({ ips }),
269
+ headers: {
270
+ "Content-Type": "application/json"
271
+ },
272
+ signal
273
+ });
274
+ }
275
+ /**
276
+ * Türkiye'deki tüm eczaneleri (nöbetçi olmayanlar dahil +30.000) sayfalı olarak döner (Premium Servis)
277
+ *
278
+ * @param options Sayfalama ve limit ayarları
279
+ *
280
+ * @example
281
+ * ```typescript
282
+ * const result = await api.getAllPharmacies({ page: 1, limit: 50 });
283
+ * ```
284
+ */
285
+ async getAllPharmacies(options) {
286
+ const query = this.buildPaginationQuery(options);
287
+ return this.request(`/pharmacies/all-pharmacies${query}`, {
288
+ method: "GET",
289
+ signal: options?.signal
290
+ });
291
+ }
292
+ /**
293
+ * Merkezi HTTP İstek Yürütücüsü (Native fetch)
294
+ */
295
+ async request(endpoint, init) {
296
+ const url = `${this.baseUrl}${endpoint.startsWith("/") ? endpoint : `/${endpoint}`}`;
297
+ const headers = {
298
+ "Accept": "application/json",
299
+ "X-Api-Key": this.apiKey,
300
+ ...this.customHeaders,
301
+ ...init.headers || {}
302
+ };
303
+ const controller = new AbortController();
304
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
305
+ if (init.signal) {
306
+ init.signal.addEventListener("abort", () => controller.abort());
307
+ }
308
+ try {
309
+ const response = await fetch(url, {
310
+ ...init,
311
+ headers,
312
+ signal: controller.signal
313
+ });
314
+ clearTimeout(timeoutId);
315
+ let data;
316
+ const contentType = response.headers.get("content-type") || "";
317
+ if (contentType.includes("application/json")) {
318
+ data = await response.json();
319
+ } else {
320
+ data = { message: await response.text() };
321
+ }
322
+ if (!response.ok) {
323
+ this.handleHttpError(response.status, data);
324
+ }
325
+ return data;
326
+ } catch (error) {
327
+ clearTimeout(timeoutId);
328
+ if (error instanceof EczaneAPIError) {
329
+ throw error;
330
+ }
331
+ if (error.name === "AbortError") {
332
+ throw new TimeoutError(`\u0130stek ${this.timeout}ms i\xE7inde yan\u0131t vermedi (Zaman A\u015F\u0131m\u0131).`);
333
+ }
334
+ throw new EczaneAPIError(
335
+ error.message || "API servisine ba\u011Flan\u0131rken beklenmedik bir hata olu\u015Ftu.",
336
+ 500,
337
+ "NETWORK_ERROR",
338
+ error
339
+ );
340
+ }
341
+ }
342
+ /**
343
+ * HTTP Hata Durumlarını Özelleştirilmiş Hata Sınıflarına Çevirir
344
+ */
345
+ handleHttpError(status, body) {
346
+ const message = body?.message || body?.error || `HTTP ${status} Hatas\u0131`;
347
+ const details = body?.details || body;
348
+ switch (status) {
349
+ case 400:
350
+ throw new InvalidRequestError(message, details);
351
+ case 401:
352
+ throw new AuthenticationError(message);
353
+ case 403:
354
+ throw new ForbiddenError(message);
355
+ case 404:
356
+ throw new NotFoundError(message);
357
+ case 429:
358
+ throw new RateLimitError(message, body?.reset_in_seconds);
359
+ default:
360
+ throw new EczaneAPIError(message, status, "API_ERROR", details);
361
+ }
362
+ }
363
+ /**
364
+ * Sayfalama query string oluşturucu
365
+ */
366
+ buildPaginationQuery(options) {
367
+ if (!options) return "";
368
+ const params = new URLSearchParams();
369
+ if (options.page && options.page > 1) {
370
+ params.append("page", options.page.toString());
371
+ }
372
+ if (options.limit) {
373
+ const limit = Math.min(Math.max(1, options.limit), 50);
374
+ params.append("limit", limit.toString());
375
+ }
376
+ const qs = params.toString();
377
+ return qs ? `?${qs}` : "";
378
+ }
379
+ };
380
+
381
+ exports.AuthenticationError = AuthenticationError;
382
+ exports.EczaneAPI = EczaneAPI;
383
+ exports.EczaneAPIError = EczaneAPIError;
384
+ exports.ForbiddenError = ForbiddenError;
385
+ exports.InvalidRequestError = InvalidRequestError;
386
+ exports.NotFoundError = NotFoundError;
387
+ exports.RateLimitError = RateLimitError;
388
+ exports.TimeoutError = TimeoutError;
389
+ exports.default = EczaneAPI;
390
+ //# sourceMappingURL=index.js.map
391
+ //# sourceMappingURL=index.js.map