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