iptuapi 2.1.0 → 2.1.2

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