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