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/README.md +502 -128
- package/dist/index.d.mts +451 -271
- package/dist/index.d.ts +451 -271
- package/dist/index.js +443 -345
- package/dist/index.mjs +442 -345
- package/package.json +29 -27
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
|
-
|
|
38
|
-
|
|
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
|
-
|
|
46
|
-
constructor(message, statusCode, requestId,
|
|
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.
|
|
46
|
+
this.responseData = responseData || {};
|
|
52
47
|
Object.setPrototypeOf(this, _IPTUAPIError.prototype);
|
|
53
48
|
}
|
|
54
|
-
|
|
55
|
-
return
|
|
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
|
|
60
|
-
super(message, 401, requestId
|
|
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 = "
|
|
68
|
-
super(message, 403, requestId
|
|
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
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 = "
|
|
100
|
-
super(message,
|
|
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
|
|
108
|
-
super(message, statusCode, requestId
|
|
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
|
-
|
|
138
|
+
isRetryable() {
|
|
113
139
|
return true;
|
|
114
140
|
}
|
|
115
141
|
};
|
|
116
142
|
var TimeoutError = class _TimeoutError extends IPTUAPIError {
|
|
117
|
-
|
|
118
|
-
constructor(message = "Timeout na
|
|
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.
|
|
147
|
+
this.timeoutSeconds = timeoutSeconds;
|
|
122
148
|
Object.setPrototypeOf(this, _TimeoutError.prototype);
|
|
123
149
|
}
|
|
124
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
143
|
-
maxDelay:
|
|
176
|
+
initialDelay: 1e3,
|
|
177
|
+
maxDelay: 3e4,
|
|
144
178
|
backoffFactor: 2,
|
|
145
|
-
|
|
179
|
+
retryableStatusCodes: [429, 500, 502, 503, 504]
|
|
146
180
|
};
|
|
147
|
-
var
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (!
|
|
160
|
-
|
|
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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
|
|
267
|
-
|
|
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
|
-
|
|
270
|
-
if (value !== void 0 && value !== null
|
|
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
|
|
276
|
-
|
|
277
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
304
|
-
this.
|
|
305
|
-
|
|
306
|
-
|
|
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
|
-
|
|
313
|
-
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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 <
|
|
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
|
-
|
|
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
|
|
326
|
+
throw new NetworkError("Erro desconhecido");
|
|
357
327
|
}
|
|
358
328
|
}
|
|
359
|
-
throw
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
*
|
|
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 -
|
|
390
|
-
* @param cidade -
|
|
391
|
-
* @
|
|
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"
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
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
|
-
*
|
|
435
|
+
* Consulta imoveis por CEP.
|
|
403
436
|
*
|
|
404
|
-
* @param cep - CEP do
|
|
405
|
-
* @param cidade -
|
|
406
|
-
* @returns Lista de
|
|
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
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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
|
|
453
|
+
* @returns Informacoes de zoneamento
|
|
422
454
|
*/
|
|
423
455
|
async consultaZoneamento(latitude, longitude) {
|
|
424
|
-
return this.
|
|
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
|
-
*
|
|
434
|
-
*
|
|
463
|
+
* Calcula estimativa de valor de mercado.
|
|
464
|
+
* Requer plano Pro ou superior.
|
|
435
465
|
*
|
|
436
|
-
* @param params -
|
|
437
|
-
* @returns
|
|
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.
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
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
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
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
|
-
*
|
|
459
|
-
*
|
|
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
|
|
462
|
-
* @returns
|
|
507
|
+
* @param params - Parametros da avaliacao (sql OU logradouro+numero)
|
|
508
|
+
* @returns Avaliacao completa do imovel
|
|
463
509
|
*/
|
|
464
|
-
async
|
|
465
|
-
|
|
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/
|
|
526
|
+
"/valuation/evaluate",
|
|
468
527
|
void 0,
|
|
469
|
-
|
|
528
|
+
body
|
|
470
529
|
);
|
|
471
530
|
}
|
|
531
|
+
// ==================== ITBI ====================
|
|
472
532
|
/**
|
|
473
|
-
*
|
|
533
|
+
* Consulta status de transacao ITBI.
|
|
474
534
|
*
|
|
475
|
-
* @param
|
|
476
|
-
* @param
|
|
477
|
-
* @
|
|
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
|
|
482
|
-
return this.
|
|
483
|
-
|
|
484
|
-
|
|
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
|
-
*
|
|
546
|
+
* Calcula valor do ITBI.
|
|
496
547
|
*
|
|
497
|
-
* @param
|
|
498
|
-
* @
|
|
499
|
-
* @returns Lista com histórico anual
|
|
548
|
+
* @param params - Parametros do calculo
|
|
549
|
+
* @returns Calculo do ITBI
|
|
500
550
|
*/
|
|
501
|
-
async
|
|
502
|
-
return this.
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
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
|
|
559
|
+
* Consulta historico de transacoes ITBI de um imovel.
|
|
560
|
+
* Requer plano Starter ou superior.
|
|
510
561
|
*
|
|
511
|
-
* @param
|
|
512
|
-
* @
|
|
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
|
|
515
|
-
const
|
|
516
|
-
return this.request(
|
|
566
|
+
async itbiHistorico(sql, cidade = "sp") {
|
|
567
|
+
const response = await this.makeRequest(
|
|
517
568
|
"GET",
|
|
518
|
-
|
|
569
|
+
"/itbi/historico",
|
|
570
|
+
{ sql, cidade }
|
|
519
571
|
);
|
|
572
|
+
return Array.isArray(response) ? response : [];
|
|
520
573
|
}
|
|
521
574
|
/**
|
|
522
|
-
*
|
|
575
|
+
* Consulta aliquotas ITBI vigentes.
|
|
523
576
|
*
|
|
524
|
-
* @param
|
|
525
|
-
* @
|
|
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
|
|
530
|
-
return this.
|
|
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
|
-
"/
|
|
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,
|