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