iptuapi 2.0.2 → 2.1.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.mjs CHANGED
@@ -1,512 +1,610 @@
1
- // src/index.ts
2
- var CidadeEnum = {
3
- SAO_PAULO: "sp",
4
- BELO_HORIZONTE: "bh",
5
- RECIFE: "recife"
6
- };
1
+ // src/errors.ts
7
2
  var IPTUAPIError = class _IPTUAPIError extends Error {
8
3
  statusCode;
9
4
  requestId;
10
- responseBody;
11
- constructor(message, statusCode, requestId, responseBody) {
5
+ responseData;
6
+ constructor(message, statusCode, requestId, responseData) {
12
7
  super(message);
13
8
  this.name = "IPTUAPIError";
14
9
  this.statusCode = statusCode;
15
10
  this.requestId = requestId;
16
- this.responseBody = responseBody;
11
+ this.responseData = responseData || {};
17
12
  Object.setPrototypeOf(this, _IPTUAPIError.prototype);
18
13
  }
19
- get isRetryable() {
20
- return this.statusCode ? [429, 500, 502, 503, 504].includes(this.statusCode) : false;
14
+ isRetryable() {
15
+ return false;
16
+ }
17
+ toJSON() {
18
+ return {
19
+ error: this.name,
20
+ message: this.message,
21
+ statusCode: this.statusCode,
22
+ requestId: this.requestId,
23
+ retryable: this.isRetryable()
24
+ };
21
25
  }
22
26
  };
23
27
  var AuthenticationError = class _AuthenticationError extends IPTUAPIError {
24
- constructor(message = "API Key inv\xE1lida ou expirada", requestId, responseBody) {
25
- super(message, 401, requestId, responseBody);
28
+ constructor(message = "API Key invalida ou ausente", requestId) {
29
+ super(message, 401, requestId);
26
30
  this.name = "AuthenticationError";
27
31
  Object.setPrototypeOf(this, _AuthenticationError.prototype);
28
32
  }
29
33
  };
30
34
  var ForbiddenError = class _ForbiddenError extends IPTUAPIError {
31
35
  requiredPlan;
32
- constructor(message = "Plano n\xE3o autorizado para este recurso", requiredPlan, requestId, responseBody) {
33
- super(message, 403, requestId, responseBody);
36
+ constructor(message = "Acesso negado", requiredPlan, requestId) {
37
+ super(message, 403, requestId);
34
38
  this.name = "ForbiddenError";
35
39
  this.requiredPlan = requiredPlan;
36
40
  Object.setPrototypeOf(this, _ForbiddenError.prototype);
37
41
  }
42
+ toJSON() {
43
+ return {
44
+ ...super.toJSON(),
45
+ requiredPlan: this.requiredPlan
46
+ };
47
+ }
38
48
  };
39
49
  var NotFoundError = class _NotFoundError extends IPTUAPIError {
40
- constructor(message = "Recurso n\xE3o encontrado", requestId, responseBody) {
41
- super(message, 404, requestId, responseBody);
50
+ resource;
51
+ constructor(message = "Recurso nao encontrado", resource, requestId) {
52
+ super(message, 404, requestId);
42
53
  this.name = "NotFoundError";
54
+ this.resource = resource;
43
55
  Object.setPrototypeOf(this, _NotFoundError.prototype);
44
56
  }
57
+ toJSON() {
58
+ return {
59
+ ...super.toJSON(),
60
+ resource: this.resource
61
+ };
62
+ }
45
63
  };
46
64
  var RateLimitError = class _RateLimitError extends IPTUAPIError {
47
65
  retryAfter;
48
- limit;
49
- remaining;
50
- constructor(message = "Limite de requisi\xE7\xF5es excedido", retryAfter, limit, remaining, requestId, responseBody) {
51
- super(message, 429, requestId, responseBody);
66
+ constructor(message = "Rate limit excedido", retryAfter = 60, requestId) {
67
+ super(message, 429, requestId);
52
68
  this.name = "RateLimitError";
53
69
  this.retryAfter = retryAfter;
54
- this.limit = limit;
55
- this.remaining = remaining;
56
70
  Object.setPrototypeOf(this, _RateLimitError.prototype);
57
71
  }
58
- get isRetryable() {
72
+ isRetryable() {
59
73
  return true;
60
74
  }
75
+ toJSON() {
76
+ return {
77
+ ...super.toJSON(),
78
+ retryAfter: this.retryAfter
79
+ };
80
+ }
61
81
  };
62
82
  var ValidationError = class _ValidationError extends IPTUAPIError {
63
83
  errors;
64
- constructor(message = "Par\xE2metros inv\xE1lidos", errors, requestId, responseBody) {
65
- super(message, 400, requestId, responseBody);
84
+ constructor(message = "Parametros invalidos", errors, statusCode = 422, requestId) {
85
+ super(message, statusCode, requestId);
66
86
  this.name = "ValidationError";
67
- this.errors = errors;
87
+ this.errors = errors || {};
68
88
  Object.setPrototypeOf(this, _ValidationError.prototype);
69
89
  }
90
+ toJSON() {
91
+ return {
92
+ ...super.toJSON(),
93
+ validationErrors: this.errors
94
+ };
95
+ }
70
96
  };
71
97
  var ServerError = class _ServerError extends IPTUAPIError {
72
- constructor(message = "Erro interno do servidor", statusCode = 500, requestId, responseBody) {
73
- super(message, statusCode, requestId, responseBody);
98
+ constructor(message = "Erro interno do servidor", statusCode = 500, requestId) {
99
+ super(message, statusCode, requestId);
74
100
  this.name = "ServerError";
75
101
  Object.setPrototypeOf(this, _ServerError.prototype);
76
102
  }
77
- get isRetryable() {
103
+ isRetryable() {
78
104
  return true;
79
105
  }
80
106
  };
81
107
  var TimeoutError = class _TimeoutError extends IPTUAPIError {
82
- timeoutMs;
83
- constructor(message = "Timeout na requisi\xE7\xE3o", timeoutMs) {
84
- super(message, 408);
108
+ timeoutSeconds;
109
+ constructor(message = "Timeout na requisicao", timeoutSeconds, requestId) {
110
+ super(message, 408, requestId);
85
111
  this.name = "TimeoutError";
86
- this.timeoutMs = timeoutMs;
112
+ this.timeoutSeconds = timeoutSeconds;
87
113
  Object.setPrototypeOf(this, _TimeoutError.prototype);
88
114
  }
89
- get isRetryable() {
115
+ isRetryable() {
90
116
  return true;
91
117
  }
118
+ toJSON() {
119
+ return {
120
+ ...super.toJSON(),
121
+ timeoutSeconds: this.timeoutSeconds
122
+ };
123
+ }
92
124
  };
93
125
  var NetworkError = class _NetworkError extends IPTUAPIError {
94
126
  originalError;
95
- constructor(message = "Erro de conex\xE3o com a API", originalError) {
96
- super(message);
127
+ constructor(message = "Erro de conexao", originalError) {
128
+ super(message, void 0, void 0);
97
129
  this.name = "NetworkError";
98
130
  this.originalError = originalError;
99
131
  Object.setPrototypeOf(this, _NetworkError.prototype);
100
132
  }
101
- get isRetryable() {
133
+ isRetryable() {
102
134
  return true;
103
135
  }
104
136
  };
137
+
138
+ // src/client.ts
105
139
  var DEFAULT_RETRY_CONFIG = {
106
140
  maxRetries: 3,
107
- initialDelay: 500,
108
- maxDelay: 1e4,
141
+ initialDelay: 1e3,
142
+ maxDelay: 3e4,
109
143
  backoffFactor: 2,
110
- retryableStatuses: [429, 500, 502, 503, 504]
144
+ retryableStatusCodes: [429, 500, 502, 503, 504]
111
145
  };
112
- var IPTUClient = class {
113
- apiKey;
114
- baseUrl;
115
- timeout;
116
- retryConfig;
117
- logger;
118
- logRequests;
119
- logResponses;
120
- userAgent;
121
- _rateLimit;
122
- _lastRequestId;
123
- constructor(apiKey, options = {}) {
124
- if (!apiKey) {
125
- throw new Error("API Key \xE9 obrigat\xF3ria");
146
+ var DEFAULT_CONFIG = {
147
+ baseUrl: "https://iptuapi.com.br/api/v1",
148
+ timeout: 3e4,
149
+ retryConfig: DEFAULT_RETRY_CONFIG
150
+ };
151
+ function toCamelCase(obj) {
152
+ const result = {};
153
+ for (const [key, value] of Object.entries(obj)) {
154
+ const camelKey = key.replace(
155
+ /_([a-z])/g,
156
+ (_, letter) => letter.toUpperCase()
157
+ );
158
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
159
+ result[camelKey] = toCamelCase(value);
160
+ } else if (Array.isArray(value)) {
161
+ result[camelKey] = value.map(
162
+ (item) => typeof item === "object" && item !== null ? toCamelCase(item) : item
163
+ );
164
+ } else {
165
+ result[camelKey] = value;
126
166
  }
127
- this.apiKey = apiKey;
128
- this.baseUrl = options.baseUrl || "https://iptuapi.com.br/api/v1";
129
- this.timeout = options.timeout || 3e4;
130
- this.retryConfig = { ...DEFAULT_RETRY_CONFIG, ...options.retry };
131
- this.logger = options.logger;
132
- this.logRequests = options.logRequests || false;
133
- this.logResponses = options.logResponses || false;
134
- this.userAgent = options.userAgent || "iptuapi-js/2.0.0";
135
- }
136
- // ===========================================================================
137
- // Properties
138
- // ===========================================================================
139
- /** Rate limit info from last request */
140
- get rateLimit() {
141
- return this._rateLimit;
142
- }
143
- /** Request ID from last request (useful for support) */
144
- get lastRequestId() {
145
- return this._lastRequestId;
146
167
  }
147
- // ===========================================================================
148
- // Private Methods
149
- // ===========================================================================
150
- log(level, message, ...args) {
151
- if (this.logger && this.logger[level]) {
152
- this.logger[level](message, ...args);
168
+ return result;
169
+ }
170
+ function toSnakeCase(obj) {
171
+ const result = {};
172
+ for (const [key, value] of Object.entries(obj)) {
173
+ const snakeKey = key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
174
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
175
+ result[snakeKey] = toSnakeCase(value);
176
+ } else if (Array.isArray(value)) {
177
+ result[snakeKey] = value.map(
178
+ (item) => typeof item === "object" && item !== null ? toSnakeCase(item) : item
179
+ );
180
+ } else {
181
+ result[snakeKey] = value;
153
182
  }
154
183
  }
155
- sleep(ms) {
156
- return new Promise((resolve) => setTimeout(resolve, ms));
157
- }
158
- calculateDelay(attempt) {
159
- const delay = this.retryConfig.initialDelay * Math.pow(this.retryConfig.backoffFactor, attempt);
160
- return Math.min(delay, this.retryConfig.maxDelay);
184
+ return result;
185
+ }
186
+ var IPTUClient = class {
187
+ apiKey;
188
+ config;
189
+ _rateLimitInfo = null;
190
+ _lastRequestId = null;
191
+ /**
192
+ * Cria uma nova instancia do cliente.
193
+ *
194
+ * @param apiKey - Chave de API para autenticacao
195
+ * @param options - Opcoes de configuracao
196
+ */
197
+ constructor(apiKey, options) {
198
+ this.apiKey = apiKey;
199
+ this.config = {
200
+ baseUrl: options?.baseUrl || DEFAULT_CONFIG.baseUrl,
201
+ timeout: options?.timeout || DEFAULT_CONFIG.timeout,
202
+ retryConfig: {
203
+ ...DEFAULT_RETRY_CONFIG,
204
+ ...options?.retryConfig
205
+ }
206
+ };
161
207
  }
162
- extractRateLimit(headers) {
163
- const limit = headers.get("X-RateLimit-Limit");
164
- const remaining = headers.get("X-RateLimit-Remaining");
165
- const reset = headers.get("X-RateLimit-Reset");
166
- if (limit && remaining && reset) {
167
- const resetTimestamp = parseInt(reset, 10);
168
- return {
169
- limit: parseInt(limit, 10),
170
- remaining: parseInt(remaining, 10),
171
- reset: resetTimestamp,
172
- resetDate: new Date(resetTimestamp * 1e3)
173
- };
174
- }
175
- return void 0;
208
+ /**
209
+ * Retorna informacoes do rate limit da ultima requisicao.
210
+ */
211
+ get rateLimitInfo() {
212
+ return this._rateLimitInfo;
176
213
  }
177
- async handleErrorResponse(response, requestId) {
178
- let body = {};
179
- try {
180
- body = await response.json();
181
- } catch {
182
- body = { detail: response.statusText };
183
- }
184
- let message;
185
- const detail = body.detail;
186
- if (detail && typeof detail === "object") {
187
- const detailObj = detail;
188
- message = detailObj.error || detailObj.detail || detailObj.message || JSON.stringify(detail);
189
- } else {
190
- message = detail || `HTTP ${response.status}`;
191
- }
192
- switch (response.status) {
193
- case 400:
194
- case 422:
195
- throw new ValidationError(
196
- message,
197
- body.errors,
198
- requestId,
199
- body
200
- );
201
- case 401:
202
- throw new AuthenticationError(message, requestId, body);
203
- case 403:
204
- throw new ForbiddenError(
205
- message,
206
- body.required_plan,
207
- requestId,
208
- body
209
- );
210
- case 404:
211
- throw new NotFoundError(message, requestId, body);
212
- case 429:
213
- const retryAfter = response.headers.get("Retry-After");
214
- throw new RateLimitError(
215
- message,
216
- retryAfter ? parseInt(retryAfter, 10) : void 0,
217
- this._rateLimit?.limit,
218
- this._rateLimit?.remaining,
219
- requestId,
220
- body
221
- );
222
- case 500:
223
- case 502:
224
- case 503:
225
- case 504:
226
- throw new ServerError(message, response.status, requestId, body);
227
- default:
228
- throw new IPTUAPIError(message, response.status, requestId, body);
229
- }
214
+ /**
215
+ * Retorna o ID da ultima requisicao.
216
+ */
217
+ get lastRequestId() {
218
+ return this._lastRequestId;
230
219
  }
231
- async request(method, endpoint, params, body) {
232
- const url = new URL(`${this.baseUrl}${endpoint}`);
220
+ /**
221
+ * Executa uma requisicao HTTP com retry.
222
+ */
223
+ async makeRequest(method, endpoint, params, body) {
224
+ const url = new URL(`${this.config.baseUrl}/${endpoint.replace(/^\//, "")}`);
233
225
  if (params) {
234
- Object.entries(params).forEach(([key, value]) => {
235
- if (value !== void 0 && value !== null && value !== "") {
226
+ for (const [key, value] of Object.entries(params)) {
227
+ if (value !== void 0 && value !== null) {
236
228
  url.searchParams.append(key, String(value));
237
229
  }
238
- });
230
+ }
239
231
  }
240
- const headers = {
241
- "X-API-Key": this.apiKey,
242
- "Content-Type": "application/json",
243
- Accept: "application/json",
244
- "User-Agent": this.userAgent
245
- };
246
- let lastError;
247
- let attempt = 0;
248
- while (attempt <= this.retryConfig.maxRetries) {
232
+ const { maxRetries, initialDelay, maxDelay, backoffFactor } = this.config.retryConfig;
233
+ let delay = initialDelay;
234
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
249
235
  const controller = new AbortController();
250
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
236
+ const timeoutId = setTimeout(
237
+ () => controller.abort(),
238
+ this.config.timeout
239
+ );
251
240
  try {
252
- if (this.logRequests) {
253
- this.log(
254
- "debug",
255
- `Request: ${method} ${url}`,
256
- params ? { params } : {},
257
- body ? { body } : {}
258
- );
259
- }
260
- const startTime = Date.now();
261
241
  const response = await fetch(url.toString(), {
262
242
  method,
263
- headers,
264
- body: body ? JSON.stringify(body) : void 0,
243
+ headers: {
244
+ "X-API-Key": this.apiKey,
245
+ "Content-Type": "application/json",
246
+ Accept: "application/json",
247
+ "User-Agent": "iptuapi-js/1.0.0"
248
+ },
249
+ body: body ? JSON.stringify(toSnakeCase(body)) : void 0,
265
250
  signal: controller.signal
266
251
  });
267
252
  clearTimeout(timeoutId);
268
- const elapsedMs = Date.now() - startTime;
269
- this._rateLimit = this.extractRateLimit(response.headers);
270
- this._lastRequestId = response.headers.get("X-Request-ID") || void 0;
271
- if (this.logResponses) {
272
- this.log(
273
- "debug",
274
- `Response: ${response.status} ${url} (${elapsedMs}ms)`
275
- );
276
- }
277
- if (response.ok) {
278
- return await response.json();
253
+ this.updateRateLimitInfo(response.headers);
254
+ this._lastRequestId = response.headers.get("X-Request-ID");
255
+ if (!response.ok) {
256
+ await this.handleError(response);
279
257
  }
280
- if (this.retryConfig.retryableStatuses.includes(response.status) && attempt < this.retryConfig.maxRetries) {
281
- const delay = this.calculateDelay(attempt);
282
- this.log(
283
- "warn",
284
- `Request failed with ${response.status}, retrying in ${delay}ms (attempt ${attempt + 1}/${this.retryConfig.maxRetries})`
285
- );
286
- await this.sleep(delay);
287
- attempt++;
288
- continue;
289
- }
290
- await this.handleErrorResponse(response, this._lastRequestId);
258
+ const data = await response.json();
259
+ return toCamelCase(data.data || data);
291
260
  } catch (error) {
292
261
  clearTimeout(timeoutId);
293
262
  if (error instanceof IPTUAPIError) {
263
+ if (error.isRetryable() && attempt < maxRetries && this.config.retryConfig.retryableStatusCodes.includes(
264
+ error.statusCode || 0
265
+ )) {
266
+ await this.sleep(delay);
267
+ delay = Math.min(delay * backoffFactor, maxDelay);
268
+ continue;
269
+ }
294
270
  throw error;
295
271
  }
296
272
  if (error instanceof Error) {
297
273
  if (error.name === "AbortError") {
298
- lastError = new TimeoutError(
299
- `Timeout ap\xF3s ${this.timeout}ms`,
300
- this.timeout
301
- );
302
- } else if (error.message.includes("fetch") || error.message.includes("network")) {
303
- lastError = new NetworkError(
304
- `Erro de conex\xE3o: ${error.message}`,
305
- error
274
+ if (attempt < maxRetries) {
275
+ await this.sleep(delay);
276
+ delay = Math.min(delay * backoffFactor, maxDelay);
277
+ continue;
278
+ }
279
+ throw new TimeoutError(
280
+ `Timeout apos ${this.config.timeout}ms`,
281
+ this.config.timeout / 1e3
306
282
  );
307
- } else {
308
- lastError = error;
309
283
  }
310
- if (attempt < this.retryConfig.maxRetries) {
311
- const delay = this.calculateDelay(attempt);
312
- this.log(
313
- "warn",
314
- `Request failed: ${error.message}, retrying in ${delay}ms (attempt ${attempt + 1}/${this.retryConfig.maxRetries})`
315
- );
284
+ if (attempt < maxRetries) {
316
285
  await this.sleep(delay);
317
- attempt++;
286
+ delay = Math.min(delay * backoffFactor, maxDelay);
318
287
  continue;
319
288
  }
289
+ throw new NetworkError(`Erro de conexao: ${error.message}`, error);
320
290
  }
321
- throw lastError || error;
291
+ throw new NetworkError("Erro desconhecido");
322
292
  }
323
293
  }
324
- throw lastError || new IPTUAPIError("Max retries exceeded");
325
- }
326
- async consultaEndereco(paramsOrLogradouro, numero, cidade) {
327
- let params;
328
- if (typeof paramsOrLogradouro === "string") {
329
- params = {
330
- logradouro: paramsOrLogradouro,
331
- numero,
332
- cidade: cidade || "sp"
333
- };
334
- } else {
335
- params = {
336
- logradouro: paramsOrLogradouro.logradouro,
337
- numero: paramsOrLogradouro.numero,
338
- complemento: paramsOrLogradouro.complemento,
339
- cidade: paramsOrLogradouro.cidade || "sp",
340
- incluir_historico: paramsOrLogradouro.incluirHistorico,
341
- incluir_comparaveis: paramsOrLogradouro.incluirComparaveis,
342
- incluir_zoneamento: paramsOrLogradouro.incluirZoneamento
294
+ throw new NetworkError("Maximo de tentativas excedido");
295
+ }
296
+ /**
297
+ * Atualiza informacoes de rate limit a partir dos headers.
298
+ */
299
+ updateRateLimitInfo(headers) {
300
+ const limit = headers.get("X-RateLimit-Limit");
301
+ const remaining = headers.get("X-RateLimit-Remaining");
302
+ const reset = headers.get("X-RateLimit-Reset");
303
+ if (limit && remaining && reset) {
304
+ this._rateLimitInfo = {
305
+ limit: parseInt(limit, 10),
306
+ remaining: parseInt(remaining, 10),
307
+ resetAt: new Date(parseInt(reset, 10) * 1e3)
343
308
  };
344
309
  }
345
- return this.request(
346
- "GET",
347
- "/consulta/endereco",
348
- params
349
- );
350
310
  }
351
311
  /**
352
- * Busca dados de IPTU por número SQL (contribuinte).
312
+ * Converte resposta HTTP em excecao apropriada.
313
+ */
314
+ async handleError(response) {
315
+ const requestId = response.headers.get("X-Request-ID") || void 0;
316
+ let data = {};
317
+ let message = "Erro desconhecido";
318
+ try {
319
+ data = await response.json();
320
+ message = data.detail || data.message || message;
321
+ } catch {
322
+ message = response.statusText || message;
323
+ }
324
+ switch (response.status) {
325
+ case 401:
326
+ throw new AuthenticationError(message, requestId);
327
+ case 403:
328
+ throw new ForbiddenError(
329
+ message,
330
+ data.required_plan,
331
+ requestId
332
+ );
333
+ case 404:
334
+ throw new NotFoundError(
335
+ message,
336
+ data.resource,
337
+ requestId
338
+ );
339
+ case 429: {
340
+ const retryAfter = parseInt(
341
+ response.headers.get("Retry-After") || "60",
342
+ 10
343
+ );
344
+ throw new RateLimitError(message, retryAfter, requestId);
345
+ }
346
+ case 400:
347
+ case 422:
348
+ throw new ValidationError(
349
+ message,
350
+ data.errors,
351
+ response.status,
352
+ requestId
353
+ );
354
+ default:
355
+ if (response.status >= 500) {
356
+ throw new ServerError(message, response.status, requestId);
357
+ }
358
+ throw new IPTUAPIError(message, response.status, requestId, data);
359
+ }
360
+ }
361
+ /**
362
+ * Aguarda um tempo em milissegundos.
363
+ */
364
+ sleep(ms) {
365
+ return new Promise((resolve) => setTimeout(resolve, ms));
366
+ }
367
+ // ==================== CONSULTAS IPTU ====================
368
+ /**
369
+ * Consulta imoveis por endereco.
353
370
  *
354
- * @param sql - Número SQL do imóvel
355
- * @param cidade - Cidade da consulta
356
- * @param options - Opções adicionais
357
- * @returns Dados completos do imóvel
371
+ * @param logradouro - Nome da rua/avenida
372
+ * @param numero - Numero do imovel
373
+ * @param cidade - Codigo da cidade (sp, bh, recife)
374
+ * @returns Lista de imoveis encontrados
358
375
  */
359
- async consultaSQL(sql, cidade = "sp", options) {
360
- return this.request("GET", `/consulta/sql/${sql}`, {
361
- cidade,
362
- incluir_historico: options?.incluirHistorico,
363
- incluir_comparaveis: options?.incluirComparaveis
376
+ async consultaEndereco(logradouro, numero, cidade = "sp") {
377
+ const response = await this.makeRequest("GET", "/consulta/endereco", {
378
+ logradouro,
379
+ numero,
380
+ cidade
364
381
  });
382
+ return Array.isArray(response) ? response : [];
365
383
  }
366
384
  /**
367
- * Busca imóveis por CEP.
385
+ * Consulta imovel por numero SQL/Indice Cadastral.
386
+ * Requer plano Starter ou superior.
368
387
  *
369
- * @param cep - CEP do imóvel
370
- * @param cidade - Cidade da consulta
371
- * @returns Lista de imóveis no CEP
388
+ * @param sql - Numero SQL (SP), Indice Cadastral (BH) ou Sequencial (Recife)
389
+ * @param cidade - Codigo da cidade (sp, bh, recife)
390
+ * @returns Lista de imoveis encontrados
391
+ */
392
+ async consultaSQL(sql, cidade = "sp") {
393
+ const response = await this.makeRequest("GET", "/consulta/sql", {
394
+ sql,
395
+ cidade
396
+ });
397
+ return Array.isArray(response) ? response : [];
398
+ }
399
+ /**
400
+ * Consulta imoveis por CEP.
401
+ *
402
+ * @param cep - CEP do endereco
403
+ * @param cidade - Codigo da cidade (sp, bh, recife)
404
+ * @returns Lista de imoveis encontrados
372
405
  */
373
406
  async consultaCEP(cep, cidade = "sp") {
374
- const cleanCep = cep.replace(/\D/g, "");
375
- return this.request(
376
- "GET",
377
- `/consulta/cep/${cleanCep}`,
378
- { cidade }
379
- );
407
+ const response = await this.makeRequest("GET", "/consulta/cep", {
408
+ cep,
409
+ cidade
410
+ });
411
+ return Array.isArray(response) ? response : [];
380
412
  }
381
413
  /**
382
414
  * Consulta zoneamento por coordenadas.
383
415
  *
384
416
  * @param latitude - Latitude do ponto
385
417
  * @param longitude - Longitude do ponto
386
- * @returns Dados de zoneamento
418
+ * @returns Informacoes de zoneamento
387
419
  */
388
420
  async consultaZoneamento(latitude, longitude) {
389
- return this.request("GET", "/consulta/zoneamento", {
390
- latitude,
391
- longitude
421
+ return this.makeRequest("GET", "/consulta/zoneamento", {
422
+ lat: latitude,
423
+ lng: longitude
392
424
  });
393
425
  }
394
- // ===========================================================================
395
- // Valuation Endpoints (Pro+)
396
- // ===========================================================================
426
+ // ==================== VALUATION ====================
397
427
  /**
398
- * Estima o valor de mercado do imóvel usando ML.
399
- * Disponível apenas para planos Pro e Enterprise.
428
+ * Calcula estimativa de valor de mercado.
429
+ * Requer plano Pro ou superior.
400
430
  *
401
- * @param params - Parâmetros do imóvel
402
- * @returns Estimativa de valor
403
- * @throws {ForbiddenError} Se o plano não permitir
431
+ * @param params - Parametros da avaliacao
432
+ * @returns Avaliacao de mercado
404
433
  */
405
434
  async valuationEstimate(params) {
406
- return this.request(
407
- "POST",
408
- "/valuation/estimate",
409
- void 0,
435
+ return this.makeRequest("GET", "/valuation/estimate", {
436
+ area_terreno: params.areaTerreno,
437
+ area_construida: params.areaConstruida,
438
+ bairro: params.bairro,
439
+ cidade: params.cidade || "sp",
440
+ zona: params.zona,
441
+ tipo_uso: params.tipoUso,
442
+ tipo_padrao: params.tipoPadrao,
443
+ ano_construcao: params.anoConstrucao
444
+ });
445
+ }
446
+ /**
447
+ * Busca imoveis comparaveis.
448
+ * Requer plano Pro ou superior.
449
+ *
450
+ * @param params - Parametros da busca
451
+ * @returns Lista de imoveis comparaveis
452
+ */
453
+ async valuationComparables(params) {
454
+ const response = await this.makeRequest(
455
+ "GET",
456
+ "/valuation/comparables",
410
457
  {
411
- area_terreno: params.area_terreno,
412
- area_construida: params.area_construida,
413
458
  bairro: params.bairro,
414
- zona: params.zona,
415
- tipo_uso: params.tipo_uso,
416
- tipo_padrao: params.tipo_padrao,
417
- ano_construcao: params.ano_construcao,
418
- cidade: params.cidade || "sp"
459
+ area_min: params.areaMin,
460
+ area_max: params.areaMax,
461
+ cidade: params.cidade || "sp",
462
+ limit: params.limit || 10
419
463
  }
420
464
  );
465
+ return Array.isArray(response) ? response : [];
421
466
  }
422
467
  /**
423
- * Valuation em lote (até 100 imóveis).
424
- * Disponível apenas para plano Enterprise.
468
+ * Avalia imovel por endereco OU SQL.
469
+ * Combina dados do modelo AVM (ML) com transacoes ITBI reais.
470
+ * Requer plano Pro ou superior.
425
471
  *
426
- * @param imoveis - Lista de imóveis para avaliar
427
- * @returns Resultados de valuation para cada imóvel
472
+ * @param params - Parametros da avaliacao (sql OU logradouro+numero)
473
+ * @returns Avaliacao completa do imovel
428
474
  */
429
- async valuationBatch(imoveis) {
430
- return this.request(
475
+ async valuationEvaluate(params) {
476
+ const body = {
477
+ cidade: params.cidade || "sp",
478
+ incluirItbi: params.incluirItbi ?? true,
479
+ incluirComparaveis: params.incluirComparaveis ?? true
480
+ };
481
+ if (params.sql) {
482
+ body.sql = params.sql;
483
+ } else {
484
+ if (params.logradouro) body.logradouro = params.logradouro;
485
+ if (params.numero !== void 0) body.numero = params.numero;
486
+ if (params.complemento) body.complemento = params.complemento;
487
+ if (params.bairro) body.bairro = params.bairro;
488
+ }
489
+ return this.makeRequest(
431
490
  "POST",
432
- "/valuation/estimate/batch",
491
+ "/valuation/evaluate",
433
492
  void 0,
434
- { imoveis }
493
+ body
435
494
  );
436
495
  }
496
+ // ==================== ITBI ====================
437
497
  /**
438
- * Busca imóveis comparáveis para análise.
498
+ * Consulta status de transacao ITBI.
439
499
  *
440
- * @param bairro - Nome do bairro
441
- * @param areaMin - Área mínima em
442
- * @param areaMax - Área máxima em m²
443
- * @param options - Opções adicionais
444
- * @returns Lista de imóveis comparáveis
500
+ * @param protocolo - Numero do protocolo ITBI
501
+ * @param cidade - Codigo da cidade (sp, bh, recife)
502
+ * @returns Status da transacao
445
503
  */
446
- async valuationComparables(bairro, areaMin, areaMax, options) {
447
- return this.request("GET", "/valuation/comparables", {
448
- bairro,
449
- area_min: areaMin,
450
- area_max: areaMax,
451
- tipo_uso: options?.tipoUso,
452
- cidade: options?.cidade || "sp",
453
- limit: options?.limit || 10
504
+ async itbiStatus(protocolo, cidade = "sp") {
505
+ return this.makeRequest("GET", "/itbi/status", {
506
+ protocolo,
507
+ cidade
454
508
  });
455
509
  }
456
- // ===========================================================================
457
- // Dados Endpoints
458
- // ===========================================================================
459
510
  /**
460
- * Histórico de valores IPTU de um imóvel.
511
+ * Calcula valor do ITBI.
461
512
  *
462
- * @param sql - Número SQL do imóvel
463
- * @param cidade - Cidade da consulta
464
- * @returns Lista com histórico anual
513
+ * @param params - Parametros do calculo
514
+ * @returns Calculo do ITBI
465
515
  */
466
- async dadosIPTUHistorico(sql, cidade = "sp") {
467
- return this.request(
468
- "GET",
469
- `/dados/iptu/historico/${sql}`,
470
- { cidade }
471
- );
516
+ async itbiCalcular(params) {
517
+ return this.makeRequest("POST", "/itbi/calcular", void 0, {
518
+ sql: params.sql,
519
+ valorTransacao: params.valorTransacao,
520
+ cidade: params.cidade || "sp"
521
+ });
472
522
  }
473
523
  /**
474
- * Consulta dados de empresa por CNPJ.
524
+ * Consulta historico de transacoes ITBI de um imovel.
525
+ * Requer plano Starter ou superior.
475
526
  *
476
- * @param cnpj - CNPJ da empresa
477
- * @returns Dados cadastrais
527
+ * @param sql - Numero SQL do imovel
528
+ * @param cidade - Codigo da cidade (sp, bh, recife)
529
+ * @returns Lista de transacoes historicas
478
530
  */
479
- async dadosCNPJ(cnpj) {
480
- const cleanCnpj = cnpj.replace(/\D/g, "");
481
- return this.request(
531
+ async itbiHistorico(sql, cidade = "sp") {
532
+ const response = await this.makeRequest(
482
533
  "GET",
483
- `/dados/cnpj/${cleanCnpj}`
534
+ "/itbi/historico",
535
+ { sql, cidade }
484
536
  );
537
+ return Array.isArray(response) ? response : [];
538
+ }
539
+ /**
540
+ * Consulta aliquotas ITBI vigentes.
541
+ *
542
+ * @param cidade - Codigo da cidade (sp, bh, recife)
543
+ * @returns Aliquotas vigentes
544
+ */
545
+ async itbiAliquotas(cidade = "sp") {
546
+ return this.makeRequest("GET", "/itbi/aliquotas", { cidade });
485
547
  }
486
548
  /**
487
- * Correção monetária pelo IPCA.
549
+ * Consulta isencoes ITBI disponiveis.
488
550
  *
489
- * @param valor - Valor a corrigir
490
- * @param dataOrigem - Data do valor original (YYYY-MM)
491
- * @param dataDestino - Data destino (default: atual)
492
- * @returns Valor corrigido e fator de correção
551
+ * @param cidade - Codigo da cidade (sp, bh, recife)
552
+ * @returns Lista de isencoes disponiveis
493
553
  */
494
- async dadosIPCACorrigir(valor, dataOrigem, dataDestino) {
495
- return this.request(
554
+ async itbiIsencoes(cidade = "sp") {
555
+ const response = await this.makeRequest(
496
556
  "GET",
497
- "/dados/ipca/corrigir",
498
- {
499
- valor,
500
- data_origem: dataOrigem,
501
- data_destino: dataDestino
502
- }
557
+ "/itbi/isencoes",
558
+ { cidade }
503
559
  );
560
+ return Array.isArray(response) ? response : [];
561
+ }
562
+ /**
563
+ * Gera guia de pagamento ITBI.
564
+ * Requer plano Starter ou superior.
565
+ *
566
+ * @param params - Parametros da guia
567
+ * @returns Guia de pagamento gerada
568
+ */
569
+ async itbiGuia(params) {
570
+ return this.makeRequest("POST", "/itbi/guia", void 0, {
571
+ sql: params.sql,
572
+ valorTransacao: params.valorTransacao,
573
+ comprador: params.comprador,
574
+ vendedor: params.vendedor,
575
+ cidade: params.cidade || "sp"
576
+ });
577
+ }
578
+ /**
579
+ * Valida autenticidade de uma guia ITBI.
580
+ *
581
+ * @param protocolo - Numero do protocolo da guia
582
+ * @param cidade - Codigo da cidade (sp, bh, recife)
583
+ * @returns Resultado da validacao
584
+ */
585
+ async itbiValidarGuia(protocolo, cidade = "sp") {
586
+ return this.makeRequest("GET", "/itbi/validar", {
587
+ protocolo,
588
+ cidade
589
+ });
590
+ }
591
+ /**
592
+ * Simula calculo de ITBI.
593
+ *
594
+ * @param params - Parametros da simulacao
595
+ * @returns Resultado da simulacao
596
+ */
597
+ async itbiSimular(params) {
598
+ return this.makeRequest("POST", "/itbi/simular", void 0, {
599
+ valorTransacao: params.valorTransacao,
600
+ cidade: params.cidade || "sp",
601
+ tipoFinanciamento: params.tipoFinanciamento,
602
+ valorFinanciado: params.valorFinanciado
603
+ });
504
604
  }
505
605
  };
506
- var index_default = IPTUClient;
507
606
  export {
508
607
  AuthenticationError,
509
- CidadeEnum,
510
608
  ForbiddenError,
511
609
  IPTUAPIError,
512
610
  IPTUClient,
@@ -515,6 +613,5 @@ export {
515
613
  RateLimitError,
516
614
  ServerError,
517
615
  TimeoutError,
518
- ValidationError,
519
- index_default as default
616
+ ValidationError
520
617
  };